aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamian Hobson-Garcia <dhobsong@igel.co.jp>2011-02-24 00:47:13 -0500
committerPaul Mundt <lethal@linux-sh.org>2011-03-16 04:27:10 -0400
commit53b5031430bb3a7941b340b453afe4eabeb5340c (patch)
treec6eff3b6345c251194c6a1e6f8ca60a5d8f20581
parent3b0fd9d75598584478d1d3f6551f8a8a9696c34e (diff)
fbdev: sh_mobile_lcdc: Add YUV framebuffer support
Supports YCbCr420sp, YCbCr422sp, and YCbCr44sp, formats (bpp = 12, 16, and 24) respectively. When double-buffering both Y planes appear before the C planes (Y-Y-C-C), as opposed to Y-C-Y-C. Set .nonstd in struct sh_mobile_lcdc_chan_cfg to enable YUV mode, and use .bpp to distiguish between the 3 modes. The value of .nonstd is copied to bits 16-31 of LDDFR in the LCDC and should be set accordingly. .nonstd must be set to 0 for RGB mode. Due to the encoding of YUV data, the framebuffer will clear to green instead of black. In YUV 420 mode, panning is only possible in 2 line increments. Additionally in YUV 420 mode the vertical resolution of the framebuffer must be an even number. Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c141
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h2
-rw-r--r--include/video/sh_mobile_lcdc.h1
3 files changed, 115 insertions, 29 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index e040e46d7d91..bf2629f83f40 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -69,6 +69,7 @@ static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
69 [LDSM1R] = 0x428, 69 [LDSM1R] = 0x428,
70 [LDSM2R] = 0x42c, 70 [LDSM2R] = 0x42c,
71 [LDSA1R] = 0x430, 71 [LDSA1R] = 0x430,
72 [LDSA2R] = 0x434,
72 [LDMLSR] = 0x438, 73 [LDMLSR] = 0x438,
73 [LDHCNR] = 0x448, 74 [LDHCNR] = 0x448,
74 [LDHSYNR] = 0x44c, 75 [LDHSYNR] = 0x44c,
@@ -153,6 +154,7 @@ static bool banked(int reg_nr)
153 case LDDFR: 154 case LDDFR:
154 case LDSM1R: 155 case LDSM1R:
155 case LDSA1R: 156 case LDSA1R:
157 case LDSA2R:
156 case LDMLSR: 158 case LDMLSR:
157 case LDHCNR: 159 case LDHCNR:
158 case LDHSYNR: 160 case LDHSYNR:
@@ -465,6 +467,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
465 struct sh_mobile_lcdc_board_cfg *board_cfg; 467 struct sh_mobile_lcdc_board_cfg *board_cfg;
466 unsigned long tmp; 468 unsigned long tmp;
467 int bpp = 0; 469 int bpp = 0;
470 unsigned long ldddsr;
468 int k, m; 471 int k, m;
469 int ret = 0; 472 int ret = 0;
470 473
@@ -543,16 +546,21 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
543 } 546 }
544 547
545 /* word and long word swap */ 548 /* word and long word swap */
546 switch (bpp) { 549 ldddsr = lcdc_read(priv, _LDDDSR);
547 case 16: 550 if (priv->ch[0].info->var.nonstd)
548 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); 551 lcdc_write(priv, _LDDDSR, ldddsr | 7);
549 break; 552 else {
550 case 24: 553 switch (bpp) {
551 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 7); 554 case 16:
552 break; 555 lcdc_write(priv, _LDDDSR, ldddsr | 6);
553 case 32: 556 break;
554 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 4); 557 case 24:
555 break; 558 lcdc_write(priv, _LDDDSR, ldddsr | 7);
559 break;
560 case 32:
561 lcdc_write(priv, _LDDDSR, ldddsr | 4);
562 break;
563 }
556 } 564 }
557 565
558 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 566 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -563,21 +571,40 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
563 571
564 /* set bpp format in PKF[4:0] */ 572 /* set bpp format in PKF[4:0] */
565 tmp = lcdc_read_chan(ch, LDDFR); 573 tmp = lcdc_read_chan(ch, LDDFR);
566 tmp &= ~0x0001001f; 574 tmp &= ~0x0003031f;
567 switch (ch->info->var.bits_per_pixel) { 575 if (ch->info->var.nonstd) {
568 case 16: 576 tmp |= (ch->info->var.nonstd << 16);
569 tmp |= 0x03; 577 switch (ch->info->var.bits_per_pixel) {
570 break; 578 case 12:
571 case 24: 579 break;
572 tmp |= 0x0b; 580 case 16:
573 break; 581 tmp |= (0x1 << 8);
574 case 32: 582 break;
575 break; 583 case 24:
584 tmp |= (0x2 << 8);
585 break;
586 }
587 } else {
588 switch (ch->info->var.bits_per_pixel) {
589 case 16:
590 tmp |= 0x03;
591 break;
592 case 24:
593 tmp |= 0x0b;
594 break;
595 case 32:
596 break;
597 }
576 } 598 }
577 lcdc_write_chan(ch, LDDFR, tmp); 599 lcdc_write_chan(ch, LDDFR, tmp);
578 600
579 /* point out our frame buffer */ 601 /* point out our frame buffer */
580 lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); 602 lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start);
603 if (ch->info->var.nonstd)
604 lcdc_write_chan(ch, LDSA2R,
605 ch->info->fix.smem_start +
606 ch->info->var.xres *
607 ch->info->var.yres_virtual);
581 608
582 /* set line size */ 609 /* set line size */
583 lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); 610 lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length);
@@ -816,9 +843,15 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
816 struct sh_mobile_lcdc_priv *priv = ch->lcdc; 843 struct sh_mobile_lcdc_priv *priv = ch->lcdc;
817 unsigned long ldrcntr; 844 unsigned long ldrcntr;
818 unsigned long new_pan_offset; 845 unsigned long new_pan_offset;
846 unsigned long base_addr_y, base_addr_c;
847 unsigned long c_offset;
819 848
820 new_pan_offset = (var->yoffset * info->fix.line_length) + 849 if (!var->nonstd)
821 (var->xoffset * (info->var.bits_per_pixel / 8)); 850 new_pan_offset = (var->yoffset * info->fix.line_length) +
851 (var->xoffset * (info->var.bits_per_pixel / 8));
852 else
853 new_pan_offset = (var->yoffset * info->fix.line_length) +
854 (var->xoffset);
822 855
823 if (new_pan_offset == ch->pan_offset) 856 if (new_pan_offset == ch->pan_offset)
824 return 0; /* No change, do nothing */ 857 return 0; /* No change, do nothing */
@@ -826,7 +859,26 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
826 ldrcntr = lcdc_read(priv, _LDRCNTR); 859 ldrcntr = lcdc_read(priv, _LDRCNTR);
827 860
828 /* Set the source address for the next refresh */ 861 /* Set the source address for the next refresh */
829 lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + new_pan_offset); 862 base_addr_y = ch->dma_handle + new_pan_offset;
863 if (var->nonstd) {
864 /* Set y offset */
865 c_offset = (var->yoffset *
866 info->fix.line_length *
867 (info->var.bits_per_pixel - 8)) / 8;
868 base_addr_c = ch->dma_handle + var->xres * var->yres_virtual +
869 c_offset;
870 /* Set x offset */
871 if (info->var.bits_per_pixel == 24)
872 base_addr_c += 2 * var->xoffset;
873 else
874 base_addr_c += var->xoffset;
875 } else
876 base_addr_c = 0;
877
878 lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
879 if (base_addr_c)
880 lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
881
830 if (lcdc_chan_is_sublcd(ch)) 882 if (lcdc_chan_is_sublcd(ch))
831 lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); 883 lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
832 else 884 else
@@ -897,7 +949,10 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
897 /* Couldn't reconfigure, hopefully, can continue as before */ 949 /* Couldn't reconfigure, hopefully, can continue as before */
898 return; 950 return;
899 951
900 info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8); 952 if (info->var.nonstd)
953 info->fix.line_length = mode1.xres;
954 else
955 info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8);
901 956
902 /* 957 /*
903 * fb_set_var() calls the notifier change internally, only if 958 * fb_set_var() calls the notifier change internally, only if
@@ -1050,8 +1105,22 @@ static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev)
1050 backlight_device_unregister(bdev); 1105 backlight_device_unregister(bdev);
1051} 1106}
1052 1107
1053static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 1108static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp,
1109 int nonstd)
1054{ 1110{
1111 if (nonstd) {
1112 switch (bpp) {
1113 case 12:
1114 case 16:
1115 case 24:
1116 var->bits_per_pixel = bpp;
1117 var->nonstd = nonstd;
1118 return 0;
1119 default:
1120 return -EINVAL;
1121 }
1122 }
1123
1055 switch (bpp) { 1124 switch (bpp) {
1056 case 16: /* PKF[4:0] = 00011 - RGB 565 */ 1125 case 16: /* PKF[4:0] = 00011 - RGB 565 */
1057 var->red.offset = 11; 1126 var->red.offset = 11;
@@ -1334,6 +1403,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1334 k < cfg->num_cfg && lcd_cfg; 1403 k < cfg->num_cfg && lcd_cfg;
1335 k++, lcd_cfg++) { 1404 k++, lcd_cfg++) {
1336 unsigned long size = lcd_cfg->yres * lcd_cfg->xres; 1405 unsigned long size = lcd_cfg->yres * lcd_cfg->xres;
1406 /* NV12 buffers must have even number of lines */
1407 if ((cfg->nonstd) && cfg->bpp == 12 &&
1408 (lcd_cfg->yres & 0x1)) {
1409 dev_err(&pdev->dev, "yres must be multiple of 2"
1410 " for YCbCr420 mode.\n");
1411 error = -EINVAL;
1412 goto err1;
1413 }
1337 1414
1338 if (size > max_size) { 1415 if (size > max_size) {
1339 max_cfg = lcd_cfg; 1416 max_cfg = lcd_cfg;
@@ -1348,7 +1425,11 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1348 max_cfg->xres, max_cfg->yres); 1425 max_cfg->xres, max_cfg->yres);
1349 1426
1350 info->fix = sh_mobile_lcdc_fix; 1427 info->fix = sh_mobile_lcdc_fix;
1351 info->fix.smem_len = max_size * (cfg->bpp / 8) * 2; 1428 info->fix.smem_len = max_size * 2 * cfg->bpp / 8;
1429
1430 /* Only pan in 2 line steps for NV12 */
1431 if (cfg->nonstd && cfg->bpp == 12)
1432 info->fix.ypanstep = 2;
1352 1433
1353 if (!mode) { 1434 if (!mode) {
1354 mode = &default_720p; 1435 mode = &default_720p;
@@ -1366,7 +1447,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1366 var->yres_virtual = var->yres * 2; 1447 var->yres_virtual = var->yres * 2;
1367 var->activate = FB_ACTIVATE_NOW; 1448 var->activate = FB_ACTIVATE_NOW;
1368 1449
1369 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); 1450 error = sh_mobile_lcdc_set_bpp(var, cfg->bpp, cfg->nonstd);
1370 if (error) 1451 if (error)
1371 break; 1452 break;
1372 1453
@@ -1390,7 +1471,11 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1390 } 1471 }
1391 1472
1392 info->fix.smem_start = ch->dma_handle; 1473 info->fix.smem_start = ch->dma_handle;
1393 info->fix.line_length = var->xres * (cfg->bpp / 8); 1474 if (var->nonstd)
1475 info->fix.line_length = var->xres;
1476 else
1477 info->fix.line_length = var->xres * (cfg->bpp / 8);
1478
1394 info->screen_base = buf; 1479 info->screen_base = buf;
1395 info->device = &pdev->dev; 1480 info->device = &pdev->dev;
1396 ch->display_var = *var; 1481 ch->display_var = *var;
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index 03a22dcbcc59..4635eed63eee 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -8,7 +8,7 @@
8 8
9/* per-channel registers */ 9/* per-channel registers */
10enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, 10enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
11 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, 11 LDSM2R, LDSA1R, LDSA2R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
12 LDHAJR, 12 LDHAJR,
13 NR_CH_REGS }; 13 NR_CH_REGS };
14 14
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index f2e6ab857fa8..2c8d369190b3 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -86,6 +86,7 @@ struct sh_mobile_lcdc_chan_cfg {
86 struct sh_mobile_lcdc_board_cfg board_cfg; 86 struct sh_mobile_lcdc_board_cfg board_cfg;
87 struct sh_mobile_lcdc_bl_info bl_info; 87 struct sh_mobile_lcdc_bl_info bl_info;
88 struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */ 88 struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */
89 int nonstd;
89}; 90};
90 91
91struct sh_mobile_lcdc_info { 92struct sh_mobile_lcdc_info {