|
|
|
@ -17,6 +17,7 @@
|
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
|
#include <linux/videodev2.h>
|
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
|
#include <linux/ioctl.h>
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
@ -102,7 +103,7 @@ struct sh_mobile_lcdc_priv {
|
|
|
|
|
struct sh_mobile_lcdc_chan ch[2];
|
|
|
|
|
struct notifier_block notifier;
|
|
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -215,6 +216,47 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
unsigned long tmp;
|
|
|
|
|
int bpp = 0;
|
|
|
|
|
int k, m;
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!bpp)
|
|
|
|
|
bpp = ch->info->var.bits_per_pixel;
|
|
|
|
|
|
|
|
|
|
/* Power supply */
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (ch->info->var.nonstd) {
|
|
|
|
|
tmp = (ch->info->var.nonstd << 16);
|
|
|
|
|
switch (ch->info->var.bits_per_pixel) {
|
|
|
|
|
case 12:
|
|
|
|
|
tmp |= LDDFR_YF_420;
|
|
|
|
|
switch (sh_mobile_format_fourcc(&ch->info->var)) {
|
|
|
|
|
case V4L2_PIX_FMT_RGB565:
|
|
|
|
|
tmp = LDDFR_PKF_RGB16;
|
|
|
|
|
break;
|
|
|
|
|
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;
|
|
|
|
|
case 16:
|
|
|
|
|
tmp |= LDDFR_YF_422;
|
|
|
|
|
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;
|
|
|
|
|
case V4L2_COLORSPACE_JPEG:
|
|
|
|
|
tmp |= LDDFR_CF0;
|
|
|
|
|
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, LDMLSR, ch->pitch);
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
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;
|
|
|
|
|
else {
|
|
|
|
|
switch (bpp) {
|
|
|
|
|
case 16:
|
|
|
|
|
tmp = LDDDSR_LS | LDDDSR_WS;
|
|
|
|
|
break;
|
|
|
|
|
case 24:
|
|
|
|
|
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
|
|
|
|
break;
|
|
|
|
|
case 32:
|
|
|
|
|
default:
|
|
|
|
|
tmp = LDDDSR_LS;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_BGR32:
|
|
|
|
|
default:
|
|
|
|
|
tmp = LDDDSR_LS;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ch->info->var.nonstd)
|
|
|
|
|
pixelformat = SH_MOBILE_MERAM_PF_RGB;
|
|
|
|
|
else if (ch->info->var.bits_per_pixel == 24)
|
|
|
|
|
pixelformat = SH_MOBILE_MERAM_PF_NV24;
|
|
|
|
|
else
|
|
|
|
|
switch (sh_mobile_format_fourcc(&ch->info->var)) {
|
|
|
|
|
case V4L2_PIX_FMT_NV12:
|
|
|
|
|
case V4L2_PIX_FMT_NV21:
|
|
|
|
|
case V4L2_PIX_FMT_NV16:
|
|
|
|
|
case V4L2_PIX_FMT_NV61:
|
|
|
|
|
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,
|
|
|
|
|
ch->info->var.yres, pixelformat,
|
|
|
|
@ -845,6 +903,7 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
|
|
|
|
|
.xpanstep = 0,
|
|
|
|
|
.ypanstep = 1,
|
|
|
|
|
.ywrapstep = 0,
|
|
|
|
|
.capabilities = FB_CAP_FOURCC,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 base_addr_y, base_addr_c;
|
|
|
|
|
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
|
|
|
|
|
+ var->xoffset * (info->var.bits_per_pixel / 8);
|
|
|
|
|
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 */
|
|
|
|
|
base_addr_y = ch->dma_handle + new_pan_offset;
|
|
|
|
|
if (info->var.nonstd) {
|
|
|
|
|
if (yuv) {
|
|
|
|
|
/* Set y offset */
|
|
|
|
|
c_offset = var->yoffset * info->fix.line_length
|
|
|
|
|
* (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
|
|
|
|
|
+ c_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;
|
|
|
|
|
else
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
|
|
|
|
|
if (info->var.nonstd)
|
|
|
|
|
if (yuv)
|
|
|
|
|
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
var->yres_virtual = var->yres;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
if (sh_mobile_format_is_fourcc(var)) {
|
|
|
|
|
switch (var->grayscale) {
|
|
|
|
|
case V4L2_PIX_FMT_NV12:
|
|
|
|
|
case V4L2_PIX_FMT_NV21:
|
|
|
|
|
var->bits_per_pixel = 12;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_RGB565:
|
|
|
|
|
case V4L2_PIX_FMT_NV16:
|
|
|
|
|
case V4L2_PIX_FMT_NV61:
|
|
|
|
|
var->bits_per_pixel = 16;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_BGR24:
|
|
|
|
|
case V4L2_PIX_FMT_NV24:
|
|
|
|
|
case V4L2_PIX_FMT_NV42:
|
|
|
|
|
var->bits_per_pixel = 24;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_BGR32:
|
|
|
|
|
var->bits_per_pixel = 32;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var->red.msb_right = 0;
|
|
|
|
|
var->green.msb_right = 0;
|
|
|
|
|
var->blue.msb_right = 0;
|
|
|
|
|
var->transp.msb_right = 0;
|
|
|
|
|
/* Default to RGB and JPEG color-spaces for RGB and YUV formats
|
|
|
|
|
* respectively.
|
|
|
|
|
*/
|
|
|
|
|
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. */
|
|
|
|
|
if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
|
|
|
|
|
info->fix.smem_len)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
/* only accept the forced_bpp for dual channel configurations */
|
|
|
|
|
if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel)
|
|
|
|
|
/* only accept the forced_fourcc for dual channel configurations */
|
|
|
|
|
if (p->forced_fourcc &&
|
|
|
|
|
p->forced_fourcc != sh_mobile_format_fourcc(var))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
@ -1158,7 +1251,7 @@ static int sh_mobile_set_par(struct fb_info *info)
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
else
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -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++) {
|
|
|
|
|
unsigned int size = mode->yres * mode->xres;
|
|
|
|
|
|
|
|
|
|
/* NV12 buffers must have even number of lines */
|
|
|
|
|
if ((cfg->nonstd) && cfg->bpp == 12 &&
|
|
|
|
|
(mode->yres & 0x1)) {
|
|
|
|
|
/* NV12/NV21 buffers must have even number of lines */
|
|
|
|
|
if ((cfg->fourcc == V4L2_PIX_FMT_NV12 ||
|
|
|
|
|
cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) {
|
|
|
|
|
dev_err(dev, "yres must be multiple of 2 for YCbCr420 "
|
|
|
|
|
"mode.\n");
|
|
|
|
|
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",
|
|
|
|
|
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. */
|
|
|
|
|
if (cfg->lcd_cfg == NULL) {
|
|
|
|
|
mode = &default_720p;
|
|
|
|
@ -1509,19 +1602,38 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
|
|
|
|
|
*/
|
|
|
|
|
var = &info->var;
|
|
|
|
|
fb_videomode_to_var(var, mode);
|
|
|
|
|
var->bits_per_pixel = cfg->bpp;
|
|
|
|
|
var->width = cfg->lcd_size_cfg.width;
|
|
|
|
|
var->height = cfg->lcd_size_cfg.height;
|
|
|
|
|
var->yres_virtual = var->yres * 2;
|
|
|
|
|
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);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
max_size = max_size * var->bits_per_pixel / 8 * 2;
|
|
|
|
|
|
|
|
|
|
/* Allocate frame buffer memory and color map. */
|
|
|
|
|
buf = dma_alloc_coherent(dev, info->fix.smem_len, &ch->dma_handle,
|
|
|
|
|
GFP_KERNEL);
|
|
|
|
|
buf = dma_alloc_coherent(dev, max_size, &ch->dma_handle, GFP_KERNEL);
|
|
|
|
|
if (!buf) {
|
|
|
|
|
dev_err(dev, "unable to allocate buffer\n");
|
|
|
|
|
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);
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
dev_err(dev, "unable to allocate cmap\n");
|
|
|
|
|
dma_free_coherent(dev, info->fix.smem_len,
|
|
|
|
|
buf, ch->dma_handle);
|
|
|
|
|
dma_free_coherent(dev, max_size, buf, ch->dma_handle);
|
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
|
else
|
|
|
|
|
info->fix.line_length = var->xres * (cfg->bpp / 8);
|
|
|
|
|
info->fix.visual = FB_VISUAL_FOURCC;
|
|
|
|
|
} else {
|
|
|
|
|
info->fix.line_length = var->xres * var->bits_per_pixel / 8;
|
|
|
|
|
info->fix.visual = FB_VISUAL_TRUECOLOR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info->screen_base = buf;
|
|
|
|
|
info->device = dev;
|
|
|
|
@ -1626,9 +1749,9 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
|
|
|
|
|
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)
|
|
|
|
|
priv->forced_bpp = pdata->ch[0].bpp;
|
|
|
|
|
priv->forced_fourcc = pdata->ch[0].fourcc;
|
|
|
|
|
|
|
|
|
|
priv->base = ioremap_nocache(res->start, resource_size(res));
|
|
|
|
|
if (!priv->base)
|
|
|
|
@ -1675,13 +1798,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
|
|
|
|
|
if (error < 0)
|
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
|
|
dev_info(info->dev,
|
|
|
|
|
"registered %s/%s as %dx%d %dbpp.\n",
|
|
|
|
|
pdev->name,
|
|
|
|
|
(ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
|
|
|
|
|
"mainlcd" : "sublcd",
|
|
|
|
|
info->var.xres, info->var.yres,
|
|
|
|
|
ch->cfg.bpp);
|
|
|
|
|
dev_info(info->dev, "registered %s/%s as %dx%d %dbpp.\n",
|
|
|
|
|
pdev->name, (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
|
|
|
|
|
"mainlcd" : "sublcd", info->var.xres, info->var.yres,
|
|
|
|
|
info->var.bits_per_pixel);
|
|
|
|
|
|
|
|
|
|
/* deferred io mode: disable clock to save power */
|
|
|
|
|
if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
|
|
|
|
|