fbdev: sh_mobile_lcdc: Support FOURCC-based format API

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
This commit is contained in:
Laurent Pinchart 2011-12-13 14:02:28 +01:00 committed by Florian Tobias Schandinat
parent 0b9eabd77f
commit edd153a3e4
10 changed files with 253 additions and 133 deletions

View File

@ -271,7 +271,7 @@ static struct sh_mobile_lcdc_info lcdc0_info = {
.flags = LCDC_FLAGS_DWPOL, .flags = LCDC_FLAGS_DWPOL,
.lcd_size_cfg.width = 44, .lcd_size_cfg.width = 44,
.lcd_size_cfg.height = 79, .lcd_size_cfg.height = 79,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.lcd_cfg = lcdc0_modes, .lcd_cfg = lcdc0_modes,
.num_cfg = ARRAY_SIZE(lcdc0_modes), .num_cfg = ARRAY_SIZE(lcdc0_modes),
.board_cfg = { .board_cfg = {

View File

@ -491,7 +491,7 @@ static struct sh_mobile_lcdc_info lcdc_info = {
.meram_dev = &meram_info, .meram_dev = &meram_info,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.lcd_cfg = ap4evb_lcdc_modes, .lcd_cfg = ap4evb_lcdc_modes,
.num_cfg = ARRAY_SIZE(ap4evb_lcdc_modes), .num_cfg = ARRAY_SIZE(ap4evb_lcdc_modes),
.meram_cfg = &lcd_meram_cfg, .meram_cfg = &lcd_meram_cfg,
@ -813,7 +813,7 @@ static struct sh_mobile_lcdc_info sh_mobile_lcdc1_info = {
.meram_dev = &meram_info, .meram_dev = &meram_info,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.interface_type = RGB24, .interface_type = RGB24,
.clock_divider = 1, .clock_divider = 1,
.flags = LCDC_FLAGS_DWPOL, .flags = LCDC_FLAGS_DWPOL,

View File

@ -388,7 +388,7 @@ static struct sh_mobile_lcdc_info lcdc_info = {
.clock_source = LCDC_CLK_BUS, .clock_source = LCDC_CLK_BUS,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.lcd_cfg = mackerel_lcdc_modes, .lcd_cfg = mackerel_lcdc_modes,
.num_cfg = ARRAY_SIZE(mackerel_lcdc_modes), .num_cfg = ARRAY_SIZE(mackerel_lcdc_modes),
.interface_type = RGB24, .interface_type = RGB24,
@ -451,7 +451,7 @@ static struct sh_mobile_lcdc_info hdmi_lcdc_info = {
.clock_source = LCDC_CLK_EXTERNAL, .clock_source = LCDC_CLK_EXTERNAL,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.interface_type = RGB24, .interface_type = RGB24,
.clock_divider = 1, .clock_divider = 1,
.flags = LCDC_FLAGS_DWPOL, .flags = LCDC_FLAGS_DWPOL,

View File

@ -207,7 +207,7 @@ static struct sh_mobile_lcdc_info lcdc_info = {
.clock_source = LCDC_CLK_EXTERNAL, .clock_source = LCDC_CLK_EXTERNAL,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.interface_type = RGB18, .interface_type = RGB18,
.clock_divider = 1, .clock_divider = 1,
.lcd_cfg = ap325rxa_lcdc_modes, .lcd_cfg = ap325rxa_lcdc_modes,

View File

@ -330,7 +330,7 @@ static struct sh_mobile_lcdc_info lcdc_info = {
.ch[0] = { .ch[0] = {
.interface_type = RGB18, .interface_type = RGB18,
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.lcd_size_cfg = { /* 7.0 inch */ .lcd_size_cfg = { /* 7.0 inch */
.width = 152, .width = 152,
.height = 91, .height = 91,

View File

@ -146,7 +146,7 @@ static struct sh_mobile_lcdc_info kfr2r09_sh_lcdc_info = {
.clock_source = LCDC_CLK_BUS, .clock_source = LCDC_CLK_BUS,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.interface_type = SYS18, .interface_type = SYS18,
.clock_divider = 6, .clock_divider = 6,
.flags = LCDC_FLAGS_DWPOL, .flags = LCDC_FLAGS_DWPOL,

View File

@ -244,7 +244,7 @@ static struct sh_mobile_lcdc_info sh_mobile_lcdc_info = {
.clock_source = LCDC_CLK_BUS, .clock_source = LCDC_CLK_BUS,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.interface_type = RGB16, .interface_type = RGB16,
.clock_divider = 2, .clock_divider = 2,
.lcd_cfg = migor_lcd_modes, .lcd_cfg = migor_lcd_modes,
@ -258,7 +258,7 @@ static struct sh_mobile_lcdc_info sh_mobile_lcdc_info = {
.clock_source = LCDC_CLK_PERIPHERAL, .clock_source = LCDC_CLK_PERIPHERAL,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.interface_type = SYS16A, .interface_type = SYS16A,
.clock_divider = 10, .clock_divider = 10,
.lcd_cfg = migor_lcd_modes, .lcd_cfg = migor_lcd_modes,

View File

@ -179,7 +179,7 @@ static struct sh_mobile_lcdc_info lcdc_info = {
.clock_source = LCDC_CLK_EXTERNAL, .clock_source = LCDC_CLK_EXTERNAL,
.ch[0] = { .ch[0] = {
.chan = LCDC_CHAN_MAINLCD, .chan = LCDC_CHAN_MAINLCD,
.bpp = 16, .fourcc = V4L2_PIX_FMT_RGB565,
.clock_divider = 1, .clock_divider = 1,
.lcd_size_cfg = { /* 7.0 inch */ .lcd_size_cfg = { /* 7.0 inch */
.width = 152, .width = 152,

View File

@ -17,6 +17,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -102,7 +103,7 @@ struct sh_mobile_lcdc_priv {
struct sh_mobile_lcdc_chan ch[2]; struct sh_mobile_lcdc_chan ch[2];
struct notifier_block notifier; struct notifier_block notifier;
int started; int started;
int forced_bpp; /* 2 channel LCDC must share bpp setting */ int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
struct sh_mobile_meram_info *meram_dev; struct sh_mobile_meram_info *meram_dev;
}; };
@ -215,6 +216,47 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
lcdc_sys_read_data, lcdc_sys_read_data,
}; };
static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var)
{
if (var->grayscale > 1)
return var->grayscale;
switch (var->bits_per_pixel) {
case 16:
return V4L2_PIX_FMT_RGB565;
case 24:
return V4L2_PIX_FMT_BGR24;
case 32:
return V4L2_PIX_FMT_BGR32;
default:
return 0;
}
}
static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var)
{
return var->grayscale > 1;
}
static bool sh_mobile_format_is_yuv(const struct fb_var_screeninfo *var)
{
if (var->grayscale <= 1)
return false;
switch (var->grayscale) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_NV24:
case V4L2_PIX_FMT_NV42:
return true;
default:
return false;
}
}
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
{ {
if (atomic_inc_and_test(&priv->hw_usecnt)) { if (atomic_inc_and_test(&priv->hw_usecnt)) {
@ -435,7 +477,6 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{ {
struct sh_mobile_lcdc_chan *ch; struct sh_mobile_lcdc_chan *ch;
unsigned long tmp; unsigned long tmp;
int bpp = 0;
int k, m; int k, m;
/* Enable LCDC channels. Read data from external memory, avoid using the /* Enable LCDC channels. Read data from external memory, avoid using the
@ -454,9 +495,6 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
if (!ch->enabled) if (!ch->enabled)
continue; continue;
if (!bpp)
bpp = ch->info->var.bits_per_pixel;
/* Power supply */ /* Power supply */
lcdc_write_chan(ch, LDPMR, 0); lcdc_write_chan(ch, LDPMR, 0);
@ -487,31 +525,37 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_geometry(ch); sh_mobile_lcdc_geometry(ch);
if (ch->info->var.nonstd) { switch (sh_mobile_format_fourcc(&ch->info->var)) {
tmp = (ch->info->var.nonstd << 16); case V4L2_PIX_FMT_RGB565:
switch (ch->info->var.bits_per_pixel) { tmp = LDDFR_PKF_RGB16;
case 12: break;
tmp |= LDDFR_YF_420; case V4L2_PIX_FMT_BGR24:
tmp = LDDFR_PKF_RGB24;
break;
case V4L2_PIX_FMT_BGR32:
tmp = LDDFR_PKF_ARGB32;
break;
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
tmp = LDDFR_CC | LDDFR_YF_420;
break;
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
tmp = LDDFR_CC | LDDFR_YF_422;
break;
case V4L2_PIX_FMT_NV24:
case V4L2_PIX_FMT_NV42:
tmp = LDDFR_CC | LDDFR_YF_444;
break;
}
if (sh_mobile_format_is_yuv(&ch->info->var)) {
switch (ch->info->var.colorspace) {
case V4L2_COLORSPACE_REC709:
tmp |= LDDFR_CF1;
break; break;
case 16: case V4L2_COLORSPACE_JPEG:
tmp |= LDDFR_YF_422; tmp |= LDDFR_CF0;
break;
case 24:
default:
tmp |= LDDFR_YF_444;
break;
}
} else {
switch (ch->info->var.bits_per_pixel) {
case 16:
tmp = LDDFR_PKF_RGB16;
break;
case 24:
tmp = LDDFR_PKF_RGB24;
break;
case 32:
default:
tmp = LDDFR_PKF_ARGB32;
break; break;
} }
} }
@ -519,7 +563,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
lcdc_write_chan(ch, LDDFR, tmp); lcdc_write_chan(ch, LDDFR, tmp);
lcdc_write_chan(ch, LDMLSR, ch->pitch); lcdc_write_chan(ch, LDMLSR, ch->pitch);
lcdc_write_chan(ch, LDSA1R, ch->base_addr_y); lcdc_write_chan(ch, LDSA1R, ch->base_addr_y);
if (ch->info->var.nonstd) if (sh_mobile_format_is_yuv(&ch->info->var))
lcdc_write_chan(ch, LDSA2R, ch->base_addr_c); lcdc_write_chan(ch, LDSA2R, ch->base_addr_c);
/* When using deferred I/O mode, configure the LCDC for one-shot /* When using deferred I/O mode, configure the LCDC for one-shot
@ -536,21 +580,23 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
} }
/* Word and long word swap. */ /* Word and long word swap. */
if (priv->ch[0].info->var.nonstd) switch (sh_mobile_format_fourcc(&priv->ch[0].info->var)) {
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_NV42:
tmp = LDDDSR_LS | LDDDSR_WS;
break;
case V4L2_PIX_FMT_BGR24:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV24:
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
else { break;
switch (bpp) { case V4L2_PIX_FMT_BGR32:
case 16: default:
tmp = LDDDSR_LS | LDDDSR_WS; tmp = LDDDSR_LS;
break; break;
case 24:
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
break;
case 32:
default:
tmp = LDDDSR_LS;
break;
}
} }
lcdc_write(priv, _LDDDSR, tmp); lcdc_write(priv, _LDDDSR, tmp);
@ -622,12 +668,24 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
ch->meram_enabled = 0; ch->meram_enabled = 0;
} }
if (!ch->info->var.nonstd) switch (sh_mobile_format_fourcc(&ch->info->var)) {
pixelformat = SH_MOBILE_MERAM_PF_RGB; case V4L2_PIX_FMT_NV12:
else if (ch->info->var.bits_per_pixel == 24) case V4L2_PIX_FMT_NV21:
pixelformat = SH_MOBILE_MERAM_PF_NV24; case V4L2_PIX_FMT_NV16:
else case V4L2_PIX_FMT_NV61:
pixelformat = SH_MOBILE_MERAM_PF_NV; pixelformat = SH_MOBILE_MERAM_PF_NV;
break;
case V4L2_PIX_FMT_NV24:
case V4L2_PIX_FMT_NV42:
pixelformat = SH_MOBILE_MERAM_PF_NV24;
break;
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_BGR24:
case V4L2_PIX_FMT_BGR32:
default:
pixelformat = SH_MOBILE_MERAM_PF_RGB;
break;
}
ret = mdev->ops->meram_register(mdev, cfg, ch->pitch, ret = mdev->ops->meram_register(mdev, cfg, ch->pitch,
ch->info->var.yres, pixelformat, ch->info->var.yres, pixelformat,
@ -845,6 +903,7 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.xpanstep = 0, .xpanstep = 0,
.ypanstep = 1, .ypanstep = 1,
.ywrapstep = 0, .ywrapstep = 0,
.capabilities = FB_CAP_FOURCC,
}; };
static void sh_mobile_lcdc_fillrect(struct fb_info *info, static void sh_mobile_lcdc_fillrect(struct fb_info *info,
@ -877,8 +936,9 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
unsigned long new_pan_offset; unsigned long new_pan_offset;
unsigned long base_addr_y, base_addr_c; unsigned long base_addr_y, base_addr_c;
unsigned long c_offset; unsigned long c_offset;
bool yuv = sh_mobile_format_is_yuv(&info->var);
if (!info->var.nonstd) if (!yuv)
new_pan_offset = var->yoffset * info->fix.line_length new_pan_offset = var->yoffset * info->fix.line_length
+ var->xoffset * (info->var.bits_per_pixel / 8); + var->xoffset * (info->var.bits_per_pixel / 8);
else else
@ -892,7 +952,7 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
/* Set the source address for the next refresh */ /* Set the source address for the next refresh */
base_addr_y = ch->dma_handle + new_pan_offset; base_addr_y = ch->dma_handle + new_pan_offset;
if (info->var.nonstd) { if (yuv) {
/* Set y offset */ /* Set y offset */
c_offset = var->yoffset * info->fix.line_length c_offset = var->yoffset * info->fix.line_length
* (info->var.bits_per_pixel - 8) / 8; * (info->var.bits_per_pixel - 8) / 8;
@ -900,7 +960,7 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
+ info->var.xres * info->var.yres_virtual + info->var.xres * info->var.yres_virtual
+ c_offset; + c_offset;
/* Set x offset */ /* Set x offset */
if (info->var.bits_per_pixel == 24) if (sh_mobile_format_fourcc(&info->var) == V4L2_PIX_FMT_NV24)
base_addr_c += 2 * var->xoffset; base_addr_c += 2 * var->xoffset;
else else
base_addr_c += var->xoffset; base_addr_c += var->xoffset;
@ -924,7 +984,7 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
ch->base_addr_c = base_addr_c; ch->base_addr_c = base_addr_c;
lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
if (info->var.nonstd) if (yuv)
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
if (lcdc_chan_is_sublcd(ch)) if (lcdc_chan_is_sublcd(ch))
@ -1100,51 +1160,84 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
if (var->yres_virtual < var->yres) if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres; var->yres_virtual = var->yres;
if (var->bits_per_pixel <= 16) { /* RGB 565 */ if (sh_mobile_format_is_fourcc(var)) {
var->bits_per_pixel = 16; switch (var->grayscale) {
var->red.offset = 11; case V4L2_PIX_FMT_NV12:
var->red.length = 5; case V4L2_PIX_FMT_NV21:
var->green.offset = 5; var->bits_per_pixel = 12;
var->green.length = 6; break;
var->blue.offset = 0; case V4L2_PIX_FMT_RGB565:
var->blue.length = 5; case V4L2_PIX_FMT_NV16:
var->transp.offset = 0; case V4L2_PIX_FMT_NV61:
var->transp.length = 0; var->bits_per_pixel = 16;
} else if (var->bits_per_pixel <= 24) { /* RGB 888 */ break;
var->bits_per_pixel = 24; case V4L2_PIX_FMT_BGR24:
var->red.offset = 16; case V4L2_PIX_FMT_NV24:
var->red.length = 8; case V4L2_PIX_FMT_NV42:
var->green.offset = 8; var->bits_per_pixel = 24;
var->green.length = 8; break;
var->blue.offset = 0; case V4L2_PIX_FMT_BGR32:
var->blue.length = 8; var->bits_per_pixel = 32;
var->transp.offset = 0; break;
var->transp.length = 0; default:
} else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ return -EINVAL;
var->bits_per_pixel = 32; }
var->red.offset = 16;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.offset = 24;
var->transp.length = 8;
} else
return -EINVAL;
var->red.msb_right = 0; /* Default to RGB and JPEG color-spaces for RGB and YUV formats
var->green.msb_right = 0; * respectively.
var->blue.msb_right = 0; */
var->transp.msb_right = 0; if (!sh_mobile_format_is_yuv(var))
var->colorspace = V4L2_COLORSPACE_SRGB;
else if (var->colorspace != V4L2_COLORSPACE_REC709)
var->colorspace = V4L2_COLORSPACE_JPEG;
} else {
if (var->bits_per_pixel <= 16) { /* RGB 565 */
var->bits_per_pixel = 16;
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
var->transp.offset = 0;
var->transp.length = 0;
} else if (var->bits_per_pixel <= 24) { /* RGB 888 */
var->bits_per_pixel = 24;
var->red.offset = 16;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.offset = 0;
var->transp.length = 0;
} else if (var->bits_per_pixel <= 32) { /* RGBA 888 */
var->bits_per_pixel = 32;
var->red.offset = 16;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.offset = 24;
var->transp.length = 8;
} else
return -EINVAL;
var->red.msb_right = 0;
var->green.msb_right = 0;
var->blue.msb_right = 0;
var->transp.msb_right = 0;
}
/* Make sure we don't exceed our allocated memory. */ /* Make sure we don't exceed our allocated memory. */
if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
info->fix.smem_len) info->fix.smem_len)
return -EINVAL; return -EINVAL;
/* only accept the forced_bpp for dual channel configurations */ /* only accept the forced_fourcc for dual channel configurations */
if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel) if (p->forced_fourcc &&
p->forced_fourcc != sh_mobile_format_fourcc(var))
return -EINVAL; return -EINVAL;
return 0; return 0;
@ -1158,7 +1251,7 @@ static int sh_mobile_set_par(struct fb_info *info)
sh_mobile_lcdc_stop(ch->lcdc); sh_mobile_lcdc_stop(ch->lcdc);
if (info->var.nonstd) if (sh_mobile_format_is_yuv(&info->var))
info->fix.line_length = info->var.xres; info->fix.line_length = info->var.xres;
else else
info->fix.line_length = info->var.xres info->fix.line_length = info->var.xres
@ -1170,6 +1263,14 @@ static int sh_mobile_set_par(struct fb_info *info)
info->fix.line_length = line_length; info->fix.line_length = line_length;
} }
if (sh_mobile_format_is_fourcc(&info->var)) {
info->fix.type = FB_TYPE_FOURCC;
info->fix.visual = FB_VISUAL_FOURCC;
} else {
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
}
return ret; return ret;
} }
@ -1464,9 +1565,9 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) { for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) {
unsigned int size = mode->yres * mode->xres; unsigned int size = mode->yres * mode->xres;
/* NV12 buffers must have even number of lines */ /* NV12/NV21 buffers must have even number of lines */
if ((cfg->nonstd) && cfg->bpp == 12 && if ((cfg->fourcc == V4L2_PIX_FMT_NV12 ||
(mode->yres & 0x1)) { cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) {
dev_err(dev, "yres must be multiple of 2 for YCbCr420 " dev_err(dev, "yres must be multiple of 2 for YCbCr420 "
"mode.\n"); "mode.\n");
return -EINVAL; return -EINVAL;
@ -1484,14 +1585,6 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
dev_dbg(dev, "Found largest videomode %ux%u\n", dev_dbg(dev, "Found largest videomode %ux%u\n",
max_mode->xres, max_mode->yres); max_mode->xres, max_mode->yres);
/* Initialize fixed screen information. Restrict pan to 2 lines steps
* for NV12.
*/
info->fix = sh_mobile_lcdc_fix;
info->fix.smem_len = max_size * 2 * cfg->bpp / 8;
if (cfg->nonstd && cfg->bpp == 12)
info->fix.ypanstep = 2;
/* Create the mode list. */ /* Create the mode list. */
if (cfg->lcd_cfg == NULL) { if (cfg->lcd_cfg == NULL) {
mode = &default_720p; mode = &default_720p;
@ -1509,19 +1602,38 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
*/ */
var = &info->var; var = &info->var;
fb_videomode_to_var(var, mode); fb_videomode_to_var(var, mode);
var->bits_per_pixel = cfg->bpp;
var->width = cfg->lcd_size_cfg.width; var->width = cfg->lcd_size_cfg.width;
var->height = cfg->lcd_size_cfg.height; var->height = cfg->lcd_size_cfg.height;
var->yres_virtual = var->yres * 2; var->yres_virtual = var->yres * 2;
var->activate = FB_ACTIVATE_NOW; var->activate = FB_ACTIVATE_NOW;
switch (cfg->fourcc) {
case V4L2_PIX_FMT_RGB565:
var->bits_per_pixel = 16;
break;
case V4L2_PIX_FMT_BGR24:
var->bits_per_pixel = 24;
break;
case V4L2_PIX_FMT_BGR32:
var->bits_per_pixel = 32;
break;
default:
var->grayscale = cfg->fourcc;
break;
}
/* Make sure the memory size check won't fail. smem_len is initialized
* later based on var.
*/
info->fix.smem_len = UINT_MAX;
ret = sh_mobile_check_var(var, info); ret = sh_mobile_check_var(var, info);
if (ret) if (ret)
return ret; return ret;
max_size = max_size * var->bits_per_pixel / 8 * 2;
/* Allocate frame buffer memory and color map. */ /* Allocate frame buffer memory and color map. */
buf = dma_alloc_coherent(dev, info->fix.smem_len, &ch->dma_handle, buf = dma_alloc_coherent(dev, max_size, &ch->dma_handle, GFP_KERNEL);
GFP_KERNEL);
if (!buf) { if (!buf) {
dev_err(dev, "unable to allocate buffer\n"); dev_err(dev, "unable to allocate buffer\n");
return -ENOMEM; return -ENOMEM;
@ -1530,16 +1642,27 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "unable to allocate cmap\n"); dev_err(dev, "unable to allocate cmap\n");
dma_free_coherent(dev, info->fix.smem_len, dma_free_coherent(dev, max_size, buf, ch->dma_handle);
buf, ch->dma_handle);
return ret; return ret;
} }
/* Initialize fixed screen information. Restrict pan to 2 lines steps
* for NV12 and NV21.
*/
info->fix = sh_mobile_lcdc_fix;
info->fix.smem_start = ch->dma_handle; info->fix.smem_start = ch->dma_handle;
if (var->nonstd) info->fix.smem_len = max_size;
if (cfg->fourcc == V4L2_PIX_FMT_NV12 ||
cfg->fourcc == V4L2_PIX_FMT_NV21)
info->fix.ypanstep = 2;
if (sh_mobile_format_is_yuv(var)) {
info->fix.line_length = var->xres; info->fix.line_length = var->xres;
else info->fix.visual = FB_VISUAL_FOURCC;
info->fix.line_length = var->xres * (cfg->bpp / 8); } else {
info->fix.line_length = var->xres * var->bits_per_pixel / 8;
info->fix.visual = FB_VISUAL_TRUECOLOR;
}
info->screen_base = buf; info->screen_base = buf;
info->device = dev; info->device = dev;
@ -1626,9 +1749,9 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1; goto err1;
} }
/* for dual channel LCDC (MAIN + SUB) force shared bpp setting */ /* for dual channel LCDC (MAIN + SUB) force shared format setting */
if (num_channels == 2) if (num_channels == 2)
priv->forced_bpp = pdata->ch[0].bpp; priv->forced_fourcc = pdata->ch[0].fourcc;
priv->base = ioremap_nocache(res->start, resource_size(res)); priv->base = ioremap_nocache(res->start, resource_size(res));
if (!priv->base) if (!priv->base)
@ -1675,13 +1798,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
if (error < 0) if (error < 0)
goto err1; goto err1;
dev_info(info->dev, dev_info(info->dev, "registered %s/%s as %dx%d %dbpp.\n",
"registered %s/%s as %dx%d %dbpp.\n", pdev->name, (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
pdev->name, "mainlcd" : "sublcd", info->var.xres, info->var.yres,
(ch->cfg.chan == LCDC_CHAN_MAINLCD) ? info->var.bits_per_pixel);
"mainlcd" : "sublcd",
info->var.xres, info->var.yres,
ch->cfg.bpp);
/* deferred io mode: disable clock to save power */ /* deferred io mode: disable clock to save power */
if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)

View File

@ -174,7 +174,8 @@ struct sh_mobile_lcdc_bl_info {
struct sh_mobile_lcdc_chan_cfg { struct sh_mobile_lcdc_chan_cfg {
int chan; int chan;
int bpp; int fourcc;
int colorspace;
int interface_type; /* selects RGBn or SYSn I/F, see above */ int interface_type; /* selects RGBn or SYSn I/F, see above */
int clock_divider; int clock_divider;
unsigned long flags; /* LCDC_FLAGS_... */ unsigned long flags; /* LCDC_FLAGS_... */
@ -184,7 +185,6 @@ struct sh_mobile_lcdc_chan_cfg {
struct sh_mobile_lcdc_board_cfg board_cfg; struct sh_mobile_lcdc_board_cfg board_cfg;
struct sh_mobile_lcdc_bl_info bl_info; struct sh_mobile_lcdc_bl_info bl_info;
struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */ struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */
int nonstd;
struct sh_mobile_meram_cfg *meram_cfg; struct sh_mobile_meram_cfg *meram_cfg;
}; };