Added code based on linaro tree:
http://git.linaro.org/kernel/linux-linaro-stable.git
with commit id:6846e7822c4cab5a84672baace3b768c2d0db142
at drivers/video/amba-clcd.c. This lets the driver set
certain tim2 register bits after reading them from
device tree.
Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>
---
.../devicetree/bindings/video/arm,pl11x.txt | 17 ++++++++-
drivers/video/fbdev/amba-clcd.c | 41 ++++++++++++++++++++++
2 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
index 3e3039a..14d6f87 100644
--- a/Documentation/devicetree/bindings/video/arm,pl11x.txt
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -35,6 +35,21 @@ Optional properties:
cell's memory interface can handle; if not present, the memory
interface is fast enough to handle all possible video modes
+- tim2: Used to set certain bits in LCDTiming2 register.
+ It can be TIM2_CLKSEL or TIM2_IOE or both
+
+ TIM2_CLKSEL: This bit drives the CLCDCLKSEL signal. It is the select
+ signal for the external LCD clock multiplexor.
+
+ TIM2_IOE: Invert output enable:
+ 0 = CLAC output pin is active HIGH in TFT mode
+ 1 = CLAC output pin is active LOW in TFT mode.
+ This bit selects the active polarity of the output enable signal in
+ TFT mode. In this mode, the CLAC pin is an enable that indicates to
+ the LCD panel when valid display data is available. In active
+ display mode, data is driven onto the LCD data lines at the
+ programmed edge of CLCP when CLAC is in its active state.
+
Required sub-nodes:
- port: describes LCD panel signals, following the common binding
@@ -76,7 +91,7 @@ Example:
clocks = <&oscclk1>, <&oscclk2>;
clock-names = "clcdclk", "apb_pclk";
max-memory-bandwidth = <94371840>; /* Bps, 1024x768@60 16bpp */
-
+ tim2 = "TIM2_CLKSEL";
port {
clcd_pads: endpoint {
remote-endpoint = <&clcd_panel>;
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 32c0b6b..4e4e50f 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -41,6 +41,44 @@
/* This is limited to 16 characters when displayed by X startup */
static const char *clcd_name = "CLCD FB";
+struct string_lookup {
+ const char *string;
+ const u32 val;
+};
+
+static const struct string_lookup tim2_lookups[] = {
+ { "TIM2_CLKSEL", TIM2_CLKSEL},
+ { "TIM2_IOE", TIM2_IOE},
+ { NULL, 0},
+};
+
+static u32 parse_setting(const struct string_lookup *lookup, const char *name)
+{
+ int i = 0;
+
+ while (lookup[i].string != NULL) {
+ if (strcmp(lookup[i].string, name) == 0)
+ return lookup[i].val;
+ ++i;
+ }
+ return 0;
+}
+
+static u32 get_string_lookup(struct device_node *node, const char *name,
+ const struct string_lookup *lookup)
+{
+ const char *string;
+ int count, i;
+ u32 ret = 0;
+
+ count = of_property_count_strings(node, name);
+ if (count >= 0)
+ for (i = 0; i < count; i++)
+ if (of_property_read_string_index(node, name, i,
+ &string) == 0)
+ ret |= parse_setting(lookup, string);
+ return ret;
+}
/*
* Unfortunately, the enable/disable functions may be called either from
* process or IRQ context, and we _need_ to delay. This is _not_ good.
@@ -626,6 +664,9 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
/* Bypass pixel clock divider, data output on the falling edge */
fb->panel->tim2 = TIM2_BCD | TIM2_IPC;
+ fb->panel->tim2 |= get_string_lookup(fb->dev->dev.of_node,
+ "tim2", tim2_lookups);
+
/* TFT display, vert. comp. interrupt at the start of the back porch */
fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
--
2.3.0
Added code to support FBIOPAN_DISPLAY. Also added yres_virtual
parameter to device tree to set the virtual y resolution
Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>
---
.../devicetree/bindings/video/arm,pl11x.txt | 4 +++
drivers/video/fbdev/amba-clcd.c | 31 +++++++++++++++++++---
2 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
index 14d6f87..2262cdb 100644
--- a/Documentation/devicetree/bindings/video/arm,pl11x.txt
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -50,6 +50,10 @@ Optional properties:
display mode, data is driven onto the LCD data lines at the
programmed edge of CLCP when CLAC is in its active state.
+- yres_virtual: Virtual Y resolution,
+ It can be used to configure a virtual y resolution. It
+ must be a value larger than the actual y resolution.
+
Required sub-nodes:
- port: describes LCD panel signals, following the common binding
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 4e4e50f..3bc09ad 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -454,6 +454,18 @@ static int clcdfb_mmap(struct fb_info *info,
return ret;
}
+static int clcdfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct clcd_fb *fb;
+
+ info->var = *var;
+ fb = to_clcd(info);
+ clcdfb_set_start(fb);
+
+ return 0;
+}
+
static struct fb_ops clcdfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = clcdfb_check_var,
@@ -464,6 +476,7 @@ static struct fb_ops clcdfb_ops = {
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_mmap = clcdfb_mmap,
+ .fb_pan_display = clcdfb_pan_display,
};
static int clcdfb_register(struct clcd_fb *fb)
@@ -517,14 +530,16 @@ static int clcdfb_register(struct clcd_fb *fb)
fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
fb->fb.fix.type_aux = 0;
fb->fb.fix.xpanstep = 0;
- fb->fb.fix.ypanstep = 0;
+ if (fb->fb.var.yres_virtual > fb->panel->mode.yres)
+ fb->fb.fix.ypanstep = 1;
+ else
+ fb->fb.fix.ypanstep = 0;
fb->fb.fix.ywrapstep = 0;
fb->fb.fix.accel = FB_ACCEL_NONE;
fb->fb.var.xres = fb->panel->mode.xres;
fb->fb.var.yres = fb->panel->mode.yres;
fb->fb.var.xres_virtual = fb->panel->mode.xres;
- fb->fb.var.yres_virtual = fb->panel->mode.yres;
fb->fb.var.bits_per_pixel = fb->panel->bpp;
fb->fb.var.grayscale = fb->panel->grayscale;
fb->fb.var.pixclock = fb->panel->mode.pixclock;
@@ -690,7 +705,7 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
struct device_node *endpoint;
int err;
unsigned int bpp;
- u32 max_bandwidth;
+ u32 max_bandwidth, yres_virtual;
u32 tft_r0b0g0[3];
fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL);
@@ -730,6 +745,14 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
fb->panel->width = -1;
fb->panel->height = -1;
+ /* if yres_virtual property is not specified in device tree,
+ * set it as the actual y resolution */
+ if (of_property_read_u32(fb->dev->dev.of_node,
+ "yres_virtual", &yres_virtual))
+ fb->fb.var.yres_virtual = fb->panel->mode.yres;
+ else
+ fb->fb.var.yres_virtual = yres_virtual;
+
if (of_property_read_u32_array(endpoint,
"arm,pl11x,tft-r0g0b0-pads",
tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0)
@@ -797,7 +820,7 @@ static int clcdfb_of_dma_setup(struct clcd_fb *fb)
if (err)
return err;
- framesize = fb->panel->mode.xres * fb->panel->mode.yres *
+ framesize = fb->panel->mode.xres * fb->fb.var.yres_virtual *
fb->panel->bpp / 8;
fb->fb.screen_base = dma_alloc_coherent(&fb->dev->dev, framesize,
&dma, GFP_KERNEL);
--
2.3.0
Added ioctl and interrupt handler functions to support FBIO_WAITFORVSYNC
Also corrected documentation to make interrupts and interrupt-names
optional as they are not required properties.
Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>0
---
.../devicetree/bindings/video/arm,pl11x.txt | 11 +--
drivers/video/fbdev/amba-clcd.c | 82 ++++++++++++++++++++++
include/linux/amba/clcd.h | 4 ++
3 files changed, 89 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
index 2262cdb..7d19024 100644
--- a/Documentation/devicetree/bindings/video/arm,pl11x.txt
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -10,14 +10,6 @@ Required properties:
- reg: base address and size of the control registers block
-- interrupt-names: either the single entry "combined" representing a
- combined interrupt output (CLCDINTR), or the four entries
- "mbe", "vcomp", "lnbu", "fuf" representing the individual
- CLCDMBEINTR, CLCDVCOMPINTR, CLCDLNBUINTR, CLCDFUFINTR interrupts
-
-- interrupts: contains an interrupt specifier for each entry in
- interrupt-names
-
- clock-names: should contain "clcdclk" and "apb_pclk"
- clocks: contains phandle and clock specifier pairs for the entries
@@ -54,6 +46,9 @@ Optional properties:
It can be used to configure a virtual y resolution. It
must be a value larger than the actual y resolution.
+- interrupts: contains an interrupt specifier for the clcd vcomp interrupt
+ This is required for the driver to handle FBIO_WAITFORVSYNC ioctls.
+
Required sub-nodes:
- port: describes LCD panel signals, following the common binding
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c
index 3bc09ad..9f7a58c 100644
--- a/drivers/video/fbdev/amba-clcd.c
+++ b/drivers/video/fbdev/amba-clcd.c
@@ -30,6 +30,8 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
#include <video/display_timing.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
@@ -466,6 +468,73 @@ static int clcdfb_pan_display(struct fb_var_screeninfo *var,
return 0;
}
+static int clcdfb_ioctl(struct fb_info *info,
+ unsigned int cmd, unsigned long args)
+{
+ struct clcd_fb *fb = to_clcd(info);
+ int retval = 0;
+ u32 val, ienb_val;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:{
+ if (fb->lcd_irq <= 0) {
+ retval = -EINVAL;
+ break;
+ }
+ /* disable Vcomp interrupts */
+ ienb_val = readl(fb->regs + fb->off_ienb);
+ ienb_val &= ~CLCD_PL111_IENB_VCOMP;
+ writel(ienb_val, fb->regs + fb->off_ienb);
+
+ /* clear Vcomp interrupt */
+ writel(CLCD_PL111_IENB_VCOMP, fb->regs + CLCD_PL111_ICR);
+
+ /* Generate Interrupt at the start of Vsync */
+ reinit_completion(&fb->wait);
+ val = readl(fb->regs + fb->off_cntl);
+ val &= ~(CNTL_LCDVCOMP(3));
+ writel(val, fb->regs + fb->off_cntl);
+
+ /* enable Vcomp interrupt */
+ ienb_val = readl(fb->regs + fb->off_ienb);
+ ienb_val |= CLCD_PL111_IENB_VCOMP;
+ writel(ienb_val, fb->regs + fb->off_ienb);
+ if (!wait_for_completion_interruptible_timeout
+ (&fb->wait, HZ/10))
+ retval = -ETIMEDOUT;
+ break;
+ }
+ default:
+ retval = -ENOIOCTLCMD;
+ break;
+ }
+ return retval;
+}
+
+static irqreturn_t clcd_interrupt(int irq, void *data)
+{
+ struct clcd_fb *fb = data;
+ u32 val;
+
+ /* check for vsync interrupt */
+ val = readl(fb->regs + CLCD_PL111_MIS);
+
+ if (val & CLCD_PL111_IENB_VCOMP) {
+ val = readl(fb->regs + fb->off_ienb);
+ val &= ~CLCD_PL111_IENB_VCOMP;
+
+ /* disable Vcomp interrupts */
+ writel(val, fb->regs + fb->off_ienb);
+
+ /* clear Vcomp interrupt */
+ writel(CLCD_PL111_IENB_VCOMP, fb->regs + CLCD_PL111_ICR);
+
+ complete(&fb->wait);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
static struct fb_ops clcdfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = clcdfb_check_var,
@@ -477,6 +546,7 @@ static struct fb_ops clcdfb_ops = {
.fb_imageblit = cfb_imageblit,
.fb_mmap = clcdfb_mmap,
.fb_pan_display = clcdfb_pan_display,
+ .fb_ioctl = clcdfb_ioctl,
};
static int clcdfb_register(struct clcd_fb *fb)
@@ -753,6 +823,18 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
else
fb->fb.var.yres_virtual = yres_virtual;
+ fb->lcd_irq = irq_of_parse_and_map(fb->dev->dev.of_node, 0);
+ if (fb->lcd_irq > 0) {
+ err = devm_request_irq(&fb->dev->dev,
+ fb->lcd_irq, clcd_interrupt,
+ IRQF_SHARED, "clcd", fb);
+ if (err < 0) {
+ dev_err(&fb->dev->dev, "unable to register CLCD interrupt\n");
+ return err;
+ }
+ init_completion(&fb->wait);
+ }
+
if (of_property_read_u32_array(endpoint,
"arm,pl11x,tft-r0g0b0-pads",
tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0)
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index e82e3ee..6a3bc2d 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -28,6 +28,8 @@
#define CLCD_PL110_UCUR 0x00000028
#define CLCD_PL110_LCUR 0x0000002C
+#define CLCD_PL111_IENB_VCOMP (1 << 3)
+
#define CLCD_PL111_CNTL 0x00000018
#define CLCD_PL111_IENB 0x0000001c
#define CLCD_PL111_RIS 0x00000020
@@ -184,6 +186,8 @@ struct clcd_fb {
u32 clcd_cntl;
u32 cmap[16];
bool clk_enabled;
+ struct completion wait;
+ int lcd_irq;
};
static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
--
2.3.0
The minimum values for timing parameters such as left margin,
right margin etc are different for STN and TFT dispalys.
This commit fixes a check that does not account for
this difference.
Reviewed-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
Signed-off-by: Arun Ramamurthy <[email protected]>
---
include/linux/amba/clcd.h | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index 6a3bc2d..0fe8a17 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -288,16 +288,28 @@ static inline int clcdfb_check(struct clcd_fb *fb, struct fb_var_screeninfo *var
var->xres_virtual = var->xres = (var->xres + 15) & ~15;
var->yres_virtual = var->yres = (var->yres + 1) & ~1;
-#define CHECK(e,l,h) (var->e < l || var->e > h)
- if (CHECK(right_margin, (5+1), 256) || /* back porch */
- CHECK(left_margin, (5+1), 256) || /* front porch */
- CHECK(hsync_len, (5+1), 256) ||
- var->xres > 4096 ||
- var->lower_margin > 255 || /* back porch */
- var->upper_margin > 255 || /* front porch */
- var->vsync_len > 32 ||
- var->yres > 1024)
- return -EINVAL;
+#define CHECK(e, l, h) (var->e < l || var->e > h)
+ if (!(fb->panel->cntl & CNTL_LCDTFT)) {
+ if (CHECK(right_margin, (5+1), 256) || /* back porch */
+ CHECK(left_margin, (5+1), 256) || /* front porch */
+ CHECK(hsync_len, (5+1), 256) ||
+ var->xres > 4096 ||
+ var->lower_margin > 255 || /* back porch */
+ var->upper_margin > 255 || /* front porch */
+ var->vsync_len > 32 ||
+ var->yres > 1024)
+ return -EINVAL;
+ } else {
+ if (CHECK(right_margin, 1, 256) || /* back porch */
+ CHECK(left_margin, 1, 256) || /* front porch */
+ CHECK(hsync_len, 1, 256) ||
+ var->xres > 4096 ||
+ var->lower_margin > 255 || /* back porch */
+ var->upper_margin > 255 || /* front porch */
+ var->vsync_len > 32 ||
+ var->yres > 1024)
+ return -EINVAL;
+ }
#undef CHECK
/* single panel mode: PCD = max(PCD, 1) */
--
2.3.0