diff options
| -rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 141 | ||||
| -rw-r--r-- | drivers/video/sh_mobile_lcdcfb.h | 2 | ||||
| -rw-r--r-- | include/video/sh_mobile_lcdc.h | 1 |
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 | ||
| 1053 | static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) | 1108 | static 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 */ |
| 10 | enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, | 10 | enum { 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 | ||
| 91 | struct sh_mobile_lcdc_info { | 92 | struct sh_mobile_lcdc_info { |
