Skip to content

Commit 7b4e9ce

Browse files
author
Russell King
committed
ARM: clcd: add method for describing display capabilities
The ARM CLCD PL110 controller in TFT mode provides two output formats based on whether the controller is in 24bpp mode or not - either 5551 or 888. PL111 augments this with a 444 and 565 modes. Some implementations provide an external MUX on the PL110 output to reassign the bits to achieve 565 mode. Provide a system of capability flags to allow the CLCD driver to work out what is supported by each panel and board, and therefore which display formats are permitted. Acked-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
1 parent 9c49e4a commit 7b4e9ce

2 files changed

Lines changed: 149 additions & 28 deletions

File tree

drivers/video/amba-clcd.c

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,23 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
120120
static int
121121
clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
122122
{
123+
u32 caps;
123124
int ret = 0;
124125

126+
if (fb->panel->caps && fb->board->caps)
127+
caps = fb->panel->caps & fb->board->caps;
128+
else {
129+
/* Old way of specifying what can be used */
130+
caps = fb->panel->cntl & CNTL_BGR ?
131+
CLCD_CAP_BGR : CLCD_CAP_RGB;
132+
/* But mask out 444 modes as they weren't supported */
133+
caps &= ~CLCD_CAP_444;
134+
}
135+
136+
/* Only TFT panels can do RGB888/BGR888 */
137+
if (!(fb->panel->cntl & CNTL_LCDTFT))
138+
caps &= ~CLCD_CAP_888;
139+
125140
memset(&var->transp, 0, sizeof(var->transp));
126141

127142
var->red.msb_right = 0;
@@ -133,30 +148,75 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
133148
case 2:
134149
case 4:
135150
case 8:
151+
/* If we can't do 5551, reject */
152+
caps &= CLCD_CAP_5551;
153+
if (!caps) {
154+
ret = -EINVAL;
155+
break;
156+
}
157+
136158
var->red.length = var->bits_per_pixel;
137159
var->red.offset = 0;
138160
var->green.length = var->bits_per_pixel;
139161
var->green.offset = 0;
140162
var->blue.length = var->bits_per_pixel;
141163
var->blue.offset = 0;
142164
break;
165+
143166
case 16:
144-
var->red.length = 5;
145-
var->blue.length = 5;
167+
/* If we can't do 444, 5551 or 565, reject */
168+
if (!(caps & (CLCD_CAP_444 | CLCD_CAP_5551 | CLCD_CAP_565))) {
169+
ret = -EINVAL;
170+
break;
171+
}
172+
146173
/*
147-
* Green length can be 5 or 6 depending whether
148-
* we're operating in RGB555 or RGB565 mode.
174+
* Green length can be 4, 5 or 6 depending whether
175+
* we're operating in 444, 5551 or 565 mode.
149176
*/
150-
if (var->green.length != 5 && var->green.length != 6)
151-
var->green.length = 6;
177+
if (var->green.length == 4 && caps & CLCD_CAP_444)
178+
caps &= CLCD_CAP_444;
179+
if (var->green.length == 5 && caps & CLCD_CAP_5551)
180+
caps &= CLCD_CAP_5551;
181+
else if (var->green.length == 6 && caps & CLCD_CAP_565)
182+
caps &= CLCD_CAP_565;
183+
else {
184+
/*
185+
* PL110 officially only supports RGB555,
186+
* but may be wired up to allow RGB565.
187+
*/
188+
if (caps & CLCD_CAP_565) {
189+
var->green.length = 6;
190+
caps &= CLCD_CAP_565;
191+
} else if (caps & CLCD_CAP_5551) {
192+
var->green.length = 5;
193+
caps &= CLCD_CAP_5551;
194+
} else {
195+
var->green.length = 4;
196+
caps &= CLCD_CAP_444;
197+
}
198+
}
199+
200+
if (var->green.length >= 5) {
201+
var->red.length = 5;
202+
var->blue.length = 5;
203+
} else {
204+
var->red.length = 4;
205+
var->blue.length = 4;
206+
}
152207
break;
153208
case 32:
154-
if (fb->panel->cntl & CNTL_LCDTFT) {
155-
var->red.length = 8;
156-
var->green.length = 8;
157-
var->blue.length = 8;
209+
/* If we can't do 888, reject */
210+
caps &= CLCD_CAP_888;
211+
if (!caps) {
212+
ret = -EINVAL;
158213
break;
159214
}
215+
216+
var->red.length = 8;
217+
var->green.length = 8;
218+
var->blue.length = 8;
219+
break;
160220
default:
161221
ret = -EINVAL;
162222
break;
@@ -168,7 +228,20 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
168228
* the bitfield length defined above.
169229
*/
170230
if (ret == 0 && var->bits_per_pixel >= 16) {
171-
if (fb->panel->cntl & CNTL_BGR) {
231+
bool bgr, rgb;
232+
233+
bgr = caps & CLCD_CAP_BGR && var->blue.offset == 0;
234+
rgb = caps & CLCD_CAP_RGB && var->red.offset == 0;
235+
236+
if (!bgr && !rgb)
237+
/*
238+
* The requested format was not possible, try just
239+
* our capabilities. One of BGR or RGB must be
240+
* supported.
241+
*/
242+
bgr = caps & CLCD_CAP_BGR;
243+
244+
if (bgr) {
172245
var->blue.offset = 0;
173246
var->green.offset = var->blue.offset + var->blue.length;
174247
var->red.offset = var->green.offset + var->green.length;

include/linux/amba/clcd.h

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#define CNTL_LCDBPP8 (3 << 1)
5454
#define CNTL_LCDBPP16 (4 << 1)
5555
#define CNTL_LCDBPP16_565 (6 << 1)
56+
#define CNTL_LCDBPP16_444 (7 << 1)
5657
#define CNTL_LCDBPP24 (5 << 1)
5758
#define CNTL_LCDBW (1 << 4)
5859
#define CNTL_LCDTFT (1 << 5)
@@ -66,13 +67,40 @@
6667
#define CNTL_LDMAFIFOTIME (1 << 15)
6768
#define CNTL_WATERMARK (1 << 16)
6869

70+
enum {
71+
/* individual formats */
72+
CLCD_CAP_RGB444 = (1 << 0),
73+
CLCD_CAP_RGB5551 = (1 << 1),
74+
CLCD_CAP_RGB565 = (1 << 2),
75+
CLCD_CAP_RGB888 = (1 << 3),
76+
CLCD_CAP_BGR444 = (1 << 4),
77+
CLCD_CAP_BGR5551 = (1 << 5),
78+
CLCD_CAP_BGR565 = (1 << 6),
79+
CLCD_CAP_BGR888 = (1 << 7),
80+
81+
/* connection layouts */
82+
CLCD_CAP_444 = CLCD_CAP_RGB444 | CLCD_CAP_BGR444,
83+
CLCD_CAP_5551 = CLCD_CAP_RGB5551 | CLCD_CAP_BGR5551,
84+
CLCD_CAP_565 = CLCD_CAP_RGB565 | CLCD_CAP_BGR565,
85+
CLCD_CAP_888 = CLCD_CAP_RGB888 | CLCD_CAP_BGR888,
86+
87+
/* red/blue ordering */
88+
CLCD_CAP_RGB = CLCD_CAP_RGB444 | CLCD_CAP_RGB5551 |
89+
CLCD_CAP_RGB565 | CLCD_CAP_RGB888,
90+
CLCD_CAP_BGR = CLCD_CAP_BGR444 | CLCD_CAP_BGR5551 |
91+
CLCD_CAP_BGR565 | CLCD_CAP_BGR888,
92+
93+
CLCD_CAP_ALL = CLCD_CAP_BGR | CLCD_CAP_RGB,
94+
};
95+
6996
struct clcd_panel {
7097
struct fb_videomode mode;
7198
signed short width; /* width in mm */
7299
signed short height; /* height in mm */
73100
u32 tim2;
74101
u32 tim3;
75102
u32 cntl;
103+
u32 caps;
76104
unsigned int bpp:8,
77105
fixedtimings:1,
78106
grayscale:1;
@@ -96,6 +124,11 @@ struct clcd_fb;
96124
struct clcd_board {
97125
const char *name;
98126

127+
/*
128+
* Optional. Hardware capability flags.
129+
*/
130+
u32 caps;
131+
99132
/*
100133
* Optional. Check whether the var structure is acceptable
101134
* for this display.
@@ -155,34 +188,35 @@ struct clcd_fb {
155188

156189
static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
157190
{
191+
struct fb_var_screeninfo *var = &fb->fb.var;
158192
u32 val, cpl;
159193

160194
/*
161195
* Program the CLCD controller registers and start the CLCD
162196
*/
163-
val = ((fb->fb.var.xres / 16) - 1) << 2;
164-
val |= (fb->fb.var.hsync_len - 1) << 8;
165-
val |= (fb->fb.var.right_margin - 1) << 16;
166-
val |= (fb->fb.var.left_margin - 1) << 24;
197+
val = ((var->xres / 16) - 1) << 2;
198+
val |= (var->hsync_len - 1) << 8;
199+
val |= (var->right_margin - 1) << 16;
200+
val |= (var->left_margin - 1) << 24;
167201
regs->tim0 = val;
168202

169-
val = fb->fb.var.yres;
203+
val = var->yres;
170204
if (fb->panel->cntl & CNTL_LCDDUAL)
171205
val /= 2;
172206
val -= 1;
173-
val |= (fb->fb.var.vsync_len - 1) << 10;
174-
val |= fb->fb.var.lower_margin << 16;
175-
val |= fb->fb.var.upper_margin << 24;
207+
val |= (var->vsync_len - 1) << 10;
208+
val |= var->lower_margin << 16;
209+
val |= var->upper_margin << 24;
176210
regs->tim1 = val;
177211

178212
val = fb->panel->tim2;
179-
val |= fb->fb.var.sync & FB_SYNC_HOR_HIGH_ACT ? 0 : TIM2_IHS;
180-
val |= fb->fb.var.sync & FB_SYNC_VERT_HIGH_ACT ? 0 : TIM2_IVS;
213+
val |= var->sync & FB_SYNC_HOR_HIGH_ACT ? 0 : TIM2_IHS;
214+
val |= var->sync & FB_SYNC_VERT_HIGH_ACT ? 0 : TIM2_IVS;
181215

182-
cpl = fb->fb.var.xres_virtual;
216+
cpl = var->xres_virtual;
183217
if (fb->panel->cntl & CNTL_LCDTFT) /* TFT */
184218
/* / 1 */;
185-
else if (!fb->fb.var.grayscale) /* STN color */
219+
else if (!var->grayscale) /* STN color */
186220
cpl = cpl * 8 / 3;
187221
else if (fb->panel->cntl & CNTL_LCDMONO8) /* STN monochrome, 8bit */
188222
cpl /= 8;
@@ -194,10 +228,22 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
194228
regs->tim3 = fb->panel->tim3;
195229

196230
val = fb->panel->cntl;
197-
if (fb->fb.var.grayscale)
231+
if (var->grayscale)
198232
val |= CNTL_LCDBW;
199233

200-
switch (fb->fb.var.bits_per_pixel) {
234+
if (fb->panel->caps && fb->board->caps &&
235+
var->bits_per_pixel >= 16) {
236+
/*
237+
* if board and panel supply capabilities, we can support
238+
* changing BGR/RGB depending on supplied parameters
239+
*/
240+
if (var->red.offset == 0)
241+
val &= ~CNTL_BGR;
242+
else
243+
val |= CNTL_BGR;
244+
}
245+
246+
switch (var->bits_per_pixel) {
201247
case 1:
202248
val |= CNTL_LCDBPP1;
203249
break;
@@ -217,18 +263,20 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
217263
* custom external wiring.
218264
*/
219265
if (amba_part(fb->dev) == 0x110 ||
220-
fb->fb.var.green.length == 5)
266+
var->green.length == 5)
221267
val |= CNTL_LCDBPP16;
222-
else
268+
else if (var->green.length == 6)
223269
val |= CNTL_LCDBPP16_565;
270+
else
271+
val |= CNTL_LCDBPP16_444;
224272
break;
225273
case 32:
226274
val |= CNTL_LCDBPP24;
227275
break;
228276
}
229277

230278
regs->cntl = val;
231-
regs->pixclock = fb->fb.var.pixclock;
279+
regs->pixclock = var->pixclock;
232280
}
233281

234282
static inline int clcdfb_check(struct clcd_fb *fb, struct fb_var_screeninfo *var)

0 commit comments

Comments
 (0)