diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb | 44 | ||||
| -rw-r--r-- | drivers/video/sh_mipi_dsi.c | 7 | ||||
| -rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 982 | ||||
| -rw-r--r-- | include/video/sh_mobile_lcdc.h | 7 |
4 files changed, 940 insertions, 100 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb new file mode 100644 index 000000000000..2107082426da --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_alpha | ||
| 2 | Date: May 2012 | ||
| 3 | Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 4 | Description: | ||
| 5 | This file is only available on fb[0-9] devices corresponding | ||
| 6 | to overlay planes. | ||
| 7 | |||
| 8 | Stores the alpha blending value for the overlay. Values range | ||
| 9 | from 0 (transparent) to 255 (opaque). The value is ignored if | ||
| 10 | the mode is not set to Alpha Blending. | ||
| 11 | |||
| 12 | What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_mode | ||
| 13 | Date: May 2012 | ||
| 14 | Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 15 | Description: | ||
| 16 | This file is only available on fb[0-9] devices corresponding | ||
| 17 | to overlay planes. | ||
| 18 | |||
| 19 | Selects the composition mode for the overlay. Possible values | ||
| 20 | are | ||
| 21 | |||
| 22 | 0 - Alpha Blending | ||
| 23 | 1 - ROP3 | ||
| 24 | |||
| 25 | What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_position | ||
| 26 | Date: May 2012 | ||
| 27 | Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 28 | Description: | ||
| 29 | This file is only available on fb[0-9] devices corresponding | ||
| 30 | to overlay planes. | ||
| 31 | |||
| 32 | Stores the x,y overlay position on the display in pixels. The | ||
| 33 | position format is `[0-9]+,[0-9]+'. | ||
| 34 | |||
| 35 | What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_rop3 | ||
| 36 | Date: May 2012 | ||
| 37 | Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 38 | Description: | ||
| 39 | This file is only available on fb[0-9] devices corresponding | ||
| 40 | to overlay planes. | ||
| 41 | |||
| 42 | Stores the raster operation (ROP3) for the overlay. Values | ||
| 43 | range from 0 to 255. The value is ignored if the mode is not | ||
| 44 | set to ROP3. | ||
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 4c6b84488561..3951fdae5f68 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c | |||
| @@ -127,8 +127,7 @@ static void sh_mipi_shutdown(struct platform_device *pdev) | |||
| 127 | sh_mipi_dsi_enable(mipi, false); | 127 | sh_mipi_dsi_enable(mipi, false); |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | static int __init sh_mipi_setup(struct sh_mipi *mipi, | 130 | static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata) |
| 131 | struct sh_mipi_dsi_info *pdata) | ||
| 132 | { | 131 | { |
| 133 | void __iomem *base = mipi->base; | 132 | void __iomem *base = mipi->base; |
| 134 | struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; | 133 | struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; |
| @@ -551,7 +550,7 @@ efindslot: | |||
| 551 | return ret; | 550 | return ret; |
| 552 | } | 551 | } |
| 553 | 552 | ||
| 554 | static int __exit sh_mipi_remove(struct platform_device *pdev) | 553 | static int __devexit sh_mipi_remove(struct platform_device *pdev) |
| 555 | { | 554 | { |
| 556 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 555 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 557 | struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 556 | struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| @@ -592,7 +591,7 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) | |||
| 592 | } | 591 | } |
| 593 | 592 | ||
| 594 | static struct platform_driver sh_mipi_driver = { | 593 | static struct platform_driver sh_mipi_driver = { |
| 595 | .remove = __exit_p(sh_mipi_remove), | 594 | .remove = __devexit_p(sh_mipi_remove), |
| 596 | .shutdown = sh_mipi_shutdown, | 595 | .shutdown = sh_mipi_shutdown, |
| 597 | .driver = { | 596 | .driver = { |
| 598 | .name = "sh-mipi-dsi", | 597 | .name = "sh-mipi-dsi", |
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index e672698bd820..98e81b31fbc4 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <linux/backlight.h> | 12 | #include <linux/backlight.h> |
| 13 | #include <linux/clk.h> | 13 | #include <linux/clk.h> |
| 14 | #include <linux/console.h> | 14 | #include <linux/console.h> |
| 15 | #include <linux/ctype.h> | ||
| 15 | #include <linux/dma-mapping.h> | 16 | #include <linux/dma-mapping.h> |
| 16 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
| 17 | #include <linux/gpio.h> | 18 | #include <linux/gpio.h> |
| @@ -32,12 +33,176 @@ | |||
| 32 | 33 | ||
| 33 | #include "sh_mobile_lcdcfb.h" | 34 | #include "sh_mobile_lcdcfb.h" |
| 34 | 35 | ||
| 36 | /* ---------------------------------------------------------------------------- | ||
| 37 | * Overlay register definitions | ||
| 38 | */ | ||
| 39 | |||
| 40 | #define LDBCR 0xb00 | ||
| 41 | #define LDBCR_UPC(n) (1 << ((n) + 16)) | ||
| 42 | #define LDBCR_UPF(n) (1 << ((n) + 8)) | ||
| 43 | #define LDBCR_UPD(n) (1 << ((n) + 0)) | ||
| 44 | #define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00) | ||
| 45 | #define LDBBSIFR_EN (1 << 31) | ||
| 46 | #define LDBBSIFR_VS (1 << 29) | ||
| 47 | #define LDBBSIFR_BRSEL (1 << 28) | ||
| 48 | #define LDBBSIFR_MX (1 << 27) | ||
| 49 | #define LDBBSIFR_MY (1 << 26) | ||
| 50 | #define LDBBSIFR_CV3 (3 << 24) | ||
| 51 | #define LDBBSIFR_CV2 (2 << 24) | ||
| 52 | #define LDBBSIFR_CV1 (1 << 24) | ||
| 53 | #define LDBBSIFR_CV0 (0 << 24) | ||
| 54 | #define LDBBSIFR_CV_MASK (3 << 24) | ||
| 55 | #define LDBBSIFR_LAY_MASK (0xff << 16) | ||
| 56 | #define LDBBSIFR_LAY_SHIFT 16 | ||
| 57 | #define LDBBSIFR_ROP3_MASK (0xff << 16) | ||
| 58 | #define LDBBSIFR_ROP3_SHIFT 16 | ||
| 59 | #define LDBBSIFR_AL_PL8 (3 << 14) | ||
| 60 | #define LDBBSIFR_AL_PL1 (2 << 14) | ||
| 61 | #define LDBBSIFR_AL_PK (1 << 14) | ||
| 62 | #define LDBBSIFR_AL_1 (0 << 14) | ||
| 63 | #define LDBBSIFR_AL_MASK (3 << 14) | ||
| 64 | #define LDBBSIFR_SWPL (1 << 10) | ||
| 65 | #define LDBBSIFR_SWPW (1 << 9) | ||
| 66 | #define LDBBSIFR_SWPB (1 << 8) | ||
| 67 | #define LDBBSIFR_RY (1 << 7) | ||
| 68 | #define LDBBSIFR_CHRR_420 (2 << 0) | ||
| 69 | #define LDBBSIFR_CHRR_422 (1 << 0) | ||
| 70 | #define LDBBSIFR_CHRR_444 (0 << 0) | ||
| 71 | #define LDBBSIFR_RPKF_ARGB32 (0x00 << 0) | ||
| 72 | #define LDBBSIFR_RPKF_RGB16 (0x03 << 0) | ||
| 73 | #define LDBBSIFR_RPKF_RGB24 (0x0b << 0) | ||
| 74 | #define LDBBSIFR_RPKF_MASK (0x1f << 0) | ||
| 75 | #define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04) | ||
| 76 | #define LDBBSSZR_BVSS_MASK (0xfff << 16) | ||
| 77 | #define LDBBSSZR_BVSS_SHIFT 16 | ||
| 78 | #define LDBBSSZR_BHSS_MASK (0xfff << 0) | ||
| 79 | #define LDBBSSZR_BHSS_SHIFT 0 | ||
| 80 | #define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08) | ||
| 81 | #define LDBBLOCR_CVLC_MASK (0xfff << 16) | ||
| 82 | #define LDBBLOCR_CVLC_SHIFT 16 | ||
| 83 | #define LDBBLOCR_CHLC_MASK (0xfff << 0) | ||
| 84 | #define LDBBLOCR_CHLC_SHIFT 0 | ||
| 85 | #define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c) | ||
| 86 | #define LDBBSMWR_BSMWA_MASK (0xffff << 16) | ||
| 87 | #define LDBBSMWR_BSMWA_SHIFT 16 | ||
| 88 | #define LDBBSMWR_BSMW_MASK (0xffff << 0) | ||
| 89 | #define LDBBSMWR_BSMW_SHIFT 0 | ||
| 90 | #define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10) | ||
| 91 | #define LDBBSAYR_FG1A_MASK (0xff << 24) | ||
| 92 | #define LDBBSAYR_FG1A_SHIFT 24 | ||
| 93 | #define LDBBSAYR_FG1R_MASK (0xff << 16) | ||
| 94 | #define LDBBSAYR_FG1R_SHIFT 16 | ||
| 95 | #define LDBBSAYR_FG1G_MASK (0xff << 8) | ||
| 96 | #define LDBBSAYR_FG1G_SHIFT 8 | ||
| 97 | #define LDBBSAYR_FG1B_MASK (0xff << 0) | ||
| 98 | #define LDBBSAYR_FG1B_SHIFT 0 | ||
| 99 | #define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14) | ||
| 100 | #define LDBBSACR_FG2A_MASK (0xff << 24) | ||
| 101 | #define LDBBSACR_FG2A_SHIFT 24 | ||
| 102 | #define LDBBSACR_FG2R_MASK (0xff << 16) | ||
| 103 | #define LDBBSACR_FG2R_SHIFT 16 | ||
| 104 | #define LDBBSACR_FG2G_MASK (0xff << 8) | ||
| 105 | #define LDBBSACR_FG2G_SHIFT 8 | ||
| 106 | #define LDBBSACR_FG2B_MASK (0xff << 0) | ||
| 107 | #define LDBBSACR_FG2B_SHIFT 0 | ||
| 108 | #define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18) | ||
| 109 | #define LDBBSAAR_AP_MASK (0xff << 24) | ||
| 110 | #define LDBBSAAR_AP_SHIFT 24 | ||
| 111 | #define LDBBSAAR_R_MASK (0xff << 16) | ||
| 112 | #define LDBBSAAR_R_SHIFT 16 | ||
| 113 | #define LDBBSAAR_GY_MASK (0xff << 8) | ||
| 114 | #define LDBBSAAR_GY_SHIFT 8 | ||
| 115 | #define LDBBSAAR_B_MASK (0xff << 0) | ||
| 116 | #define LDBBSAAR_B_SHIFT 0 | ||
| 117 | #define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c) | ||
| 118 | #define LDBBPPCR_AP_MASK (0xff << 24) | ||
| 119 | #define LDBBPPCR_AP_SHIFT 24 | ||
| 120 | #define LDBBPPCR_R_MASK (0xff << 16) | ||
| 121 | #define LDBBPPCR_R_SHIFT 16 | ||
| 122 | #define LDBBPPCR_GY_MASK (0xff << 8) | ||
| 123 | #define LDBBPPCR_GY_SHIFT 8 | ||
| 124 | #define LDBBPPCR_B_MASK (0xff << 0) | ||
| 125 | #define LDBBPPCR_B_SHIFT 0 | ||
| 126 | #define LDBnBBGCL(n) (0xb10 + (n) * 0x04) | ||
| 127 | #define LDBBBGCL_BGA_MASK (0xff << 24) | ||
| 128 | #define LDBBBGCL_BGA_SHIFT 24 | ||
| 129 | #define LDBBBGCL_BGR_MASK (0xff << 16) | ||
| 130 | #define LDBBBGCL_BGR_SHIFT 16 | ||
| 131 | #define LDBBBGCL_BGG_MASK (0xff << 8) | ||
| 132 | #define LDBBBGCL_BGG_SHIFT 8 | ||
| 133 | #define LDBBBGCL_BGB_MASK (0xff << 0) | ||
| 134 | #define LDBBBGCL_BGB_SHIFT 0 | ||
| 135 | |||
| 35 | #define SIDE_B_OFFSET 0x1000 | 136 | #define SIDE_B_OFFSET 0x1000 |
| 36 | #define MIRROR_OFFSET 0x2000 | 137 | #define MIRROR_OFFSET 0x2000 |
| 37 | 138 | ||
| 38 | #define MAX_XRES 1920 | 139 | #define MAX_XRES 1920 |
| 39 | #define MAX_YRES 1080 | 140 | #define MAX_YRES 1080 |
| 40 | 141 | ||
| 142 | enum sh_mobile_lcdc_overlay_mode { | ||
| 143 | LCDC_OVERLAY_BLEND, | ||
| 144 | LCDC_OVERLAY_ROP3, | ||
| 145 | }; | ||
| 146 | |||
| 147 | /* | ||
| 148 | * struct sh_mobile_lcdc_overlay - LCDC display overlay | ||
| 149 | * | ||
| 150 | * @channel: LCDC channel this overlay belongs to | ||
| 151 | * @cfg: Overlay configuration | ||
| 152 | * @info: Frame buffer device | ||
| 153 | * @index: Overlay index (0-3) | ||
| 154 | * @base: Overlay registers base address | ||
| 155 | * @enabled: True if the overlay is enabled | ||
| 156 | * @mode: Overlay blending mode (alpha blend or ROP3) | ||
| 157 | * @alpha: Global alpha blending value (0-255, for alpha blending mode) | ||
| 158 | * @rop3: Raster operation (for ROP3 mode) | ||
| 159 | * @fb_mem: Frame buffer virtual memory address | ||
| 160 | * @fb_size: Frame buffer size in bytes | ||
| 161 | * @dma_handle: Frame buffer DMA address | ||
| 162 | * @base_addr_y: Overlay base address (RGB or luma component) | ||
| 163 | * @base_addr_c: Overlay base address (chroma component) | ||
| 164 | * @pan_offset: Current pan offset in bytes | ||
| 165 | * @format: Current pixelf format | ||
| 166 | * @xres: Horizontal visible resolution | ||
| 167 | * @xres_virtual: Horizontal total resolution | ||
| 168 | * @yres: Vertical visible resolution | ||
| 169 | * @yres_virtual: Vertical total resolution | ||
| 170 | * @pitch: Overlay line pitch | ||
| 171 | * @pos_x: Horizontal overlay position | ||
| 172 | * @pos_y: Vertical overlay position | ||
| 173 | */ | ||
| 174 | struct sh_mobile_lcdc_overlay { | ||
| 175 | struct sh_mobile_lcdc_chan *channel; | ||
| 176 | |||
| 177 | const struct sh_mobile_lcdc_overlay_cfg *cfg; | ||
| 178 | struct fb_info *info; | ||
| 179 | |||
| 180 | unsigned int index; | ||
| 181 | unsigned long base; | ||
| 182 | |||
| 183 | bool enabled; | ||
| 184 | enum sh_mobile_lcdc_overlay_mode mode; | ||
| 185 | unsigned int alpha; | ||
| 186 | unsigned int rop3; | ||
| 187 | |||
| 188 | void *fb_mem; | ||
| 189 | unsigned long fb_size; | ||
| 190 | |||
| 191 | dma_addr_t dma_handle; | ||
| 192 | unsigned long base_addr_y; | ||
| 193 | unsigned long base_addr_c; | ||
| 194 | unsigned long pan_offset; | ||
| 195 | |||
| 196 | const struct sh_mobile_lcdc_format_info *format; | ||
| 197 | unsigned int xres; | ||
| 198 | unsigned int xres_virtual; | ||
| 199 | unsigned int yres; | ||
| 200 | unsigned int yres_virtual; | ||
| 201 | unsigned int pitch; | ||
| 202 | int pos_x; | ||
| 203 | int pos_y; | ||
| 204 | }; | ||
| 205 | |||
| 41 | struct sh_mobile_lcdc_priv { | 206 | struct sh_mobile_lcdc_priv { |
| 42 | void __iomem *base; | 207 | void __iomem *base; |
| 43 | int irq; | 208 | int irq; |
| @@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv { | |||
| 45 | struct device *dev; | 210 | struct device *dev; |
| 46 | struct clk *dot_clk; | 211 | struct clk *dot_clk; |
| 47 | unsigned long lddckr; | 212 | unsigned long lddckr; |
| 213 | |||
| 48 | struct sh_mobile_lcdc_chan ch[2]; | 214 | struct sh_mobile_lcdc_chan ch[2]; |
| 215 | struct sh_mobile_lcdc_overlay overlays[4]; | ||
| 216 | |||
| 49 | struct notifier_block notifier; | 217 | struct notifier_block notifier; |
| 50 | int started; | 218 | int started; |
| 51 | int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ | 219 | int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ |
| @@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, | |||
| 141 | return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); | 309 | return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); |
| 142 | } | 310 | } |
| 143 | 311 | ||
| 312 | static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl, | ||
| 313 | int reg, unsigned long data) | ||
| 314 | { | ||
| 315 | iowrite32(data, ovl->channel->lcdc->base + reg); | ||
| 316 | iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET); | ||
| 317 | } | ||
| 318 | |||
| 144 | static void lcdc_write(struct sh_mobile_lcdc_priv *priv, | 319 | static void lcdc_write(struct sh_mobile_lcdc_priv *priv, |
| 145 | unsigned long reg_offs, unsigned long data) | 320 | unsigned long reg_offs, unsigned long data) |
| 146 | { | 321 | { |
| @@ -384,8 +559,8 @@ sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch, | |||
| 384 | return true; | 559 | return true; |
| 385 | } | 560 | } |
| 386 | 561 | ||
| 387 | static int sh_mobile_check_var(struct fb_var_screeninfo *var, | 562 | static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, |
| 388 | struct fb_info *info); | 563 | struct fb_info *info); |
| 389 | 564 | ||
| 390 | static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, | 565 | static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, |
| 391 | enum sh_mobile_lcdc_entity_event event, | 566 | enum sh_mobile_lcdc_entity_event event, |
| @@ -439,7 +614,7 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch, | |||
| 439 | fb_videomode_to_var(&var, mode); | 614 | fb_videomode_to_var(&var, mode); |
| 440 | var.bits_per_pixel = info->var.bits_per_pixel; | 615 | var.bits_per_pixel = info->var.bits_per_pixel; |
| 441 | var.grayscale = info->var.grayscale; | 616 | var.grayscale = info->var.grayscale; |
| 442 | ret = sh_mobile_check_var(&var, info); | 617 | ret = sh_mobile_lcdc_check_var(&var, info); |
| 443 | break; | 618 | break; |
| 444 | } | 619 | } |
| 445 | 620 | ||
| @@ -585,7 +760,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) | |||
| 585 | return IRQ_HANDLED; | 760 | return IRQ_HANDLED; |
| 586 | } | 761 | } |
| 587 | 762 | ||
| 588 | static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch) | 763 | static int sh_mobile_lcdc_wait_for_vsync(struct sh_mobile_lcdc_chan *ch) |
| 589 | { | 764 | { |
| 590 | unsigned long ldintr; | 765 | unsigned long ldintr; |
| 591 | int ret; | 766 | int ret; |
| @@ -685,8 +860,98 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) | |||
| 685 | lcdc_write_chan(ch, LDHAJR, tmp); | 860 | lcdc_write_chan(ch, LDHAJR, tmp); |
| 686 | } | 861 | } |
| 687 | 862 | ||
| 863 | static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl) | ||
| 864 | { | ||
| 865 | u32 format = 0; | ||
| 866 | |||
| 867 | if (!ovl->enabled) { | ||
| 868 | lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); | ||
| 869 | lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0); | ||
| 870 | lcdc_write(ovl->channel->lcdc, LDBCR, | ||
| 871 | LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); | ||
| 872 | return; | ||
| 873 | } | ||
| 874 | |||
| 875 | ovl->base_addr_y = ovl->dma_handle; | ||
| 876 | ovl->base_addr_c = ovl->base_addr_y + ovl->xres | ||
| 877 | * ovl->yres_virtual; | ||
| 878 | |||
| 879 | switch (ovl->mode) { | ||
| 880 | case LCDC_OVERLAY_BLEND: | ||
| 881 | format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT); | ||
| 882 | break; | ||
| 883 | |||
| 884 | case LCDC_OVERLAY_ROP3: | ||
| 885 | format = LDBBSIFR_EN | LDBBSIFR_BRSEL | ||
| 886 | | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT); | ||
| 887 | break; | ||
| 888 | } | ||
| 889 | |||
| 890 | switch (ovl->format->fourcc) { | ||
| 891 | case V4L2_PIX_FMT_RGB565: | ||
| 892 | case V4L2_PIX_FMT_NV21: | ||
| 893 | case V4L2_PIX_FMT_NV61: | ||
| 894 | case V4L2_PIX_FMT_NV42: | ||
| 895 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; | ||
| 896 | break; | ||
| 897 | case V4L2_PIX_FMT_BGR24: | ||
| 898 | case V4L2_PIX_FMT_NV12: | ||
| 899 | case V4L2_PIX_FMT_NV16: | ||
| 900 | case V4L2_PIX_FMT_NV24: | ||
| 901 | format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; | ||
| 902 | break; | ||
| 903 | case V4L2_PIX_FMT_BGR32: | ||
| 904 | default: | ||
| 905 | format |= LDBBSIFR_SWPL; | ||
| 906 | break; | ||
| 907 | } | ||
| 908 | |||
| 909 | switch (ovl->format->fourcc) { | ||
| 910 | case V4L2_PIX_FMT_RGB565: | ||
| 911 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; | ||
| 912 | break; | ||
| 913 | case V4L2_PIX_FMT_BGR24: | ||
| 914 | format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; | ||
| 915 | break; | ||
| 916 | case V4L2_PIX_FMT_BGR32: | ||
| 917 | format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; | ||
| 918 | break; | ||
| 919 | case V4L2_PIX_FMT_NV12: | ||
| 920 | case V4L2_PIX_FMT_NV21: | ||
| 921 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; | ||
| 922 | break; | ||
| 923 | case V4L2_PIX_FMT_NV16: | ||
| 924 | case V4L2_PIX_FMT_NV61: | ||
| 925 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; | ||
| 926 | break; | ||
| 927 | case V4L2_PIX_FMT_NV24: | ||
| 928 | case V4L2_PIX_FMT_NV42: | ||
| 929 | format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; | ||
| 930 | break; | ||
| 931 | } | ||
| 932 | |||
| 933 | lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); | ||
| 934 | |||
| 935 | lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format); | ||
| 936 | |||
| 937 | lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index), | ||
| 938 | (ovl->yres << LDBBSSZR_BVSS_SHIFT) | | ||
| 939 | (ovl->xres << LDBBSSZR_BHSS_SHIFT)); | ||
| 940 | lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index), | ||
| 941 | (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) | | ||
| 942 | (ovl->pos_x << LDBBLOCR_CHLC_SHIFT)); | ||
| 943 | lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index), | ||
| 944 | ovl->pitch << LDBBSMWR_BSMW_SHIFT); | ||
| 945 | |||
| 946 | lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); | ||
| 947 | lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); | ||
| 948 | |||
| 949 | lcdc_write(ovl->channel->lcdc, LDBCR, | ||
| 950 | LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); | ||
| 951 | } | ||
| 952 | |||
| 688 | /* | 953 | /* |
| 689 | * __sh_mobile_lcdc_start - Configure and tart the LCDC | 954 | * __sh_mobile_lcdc_start - Configure and start the LCDC |
| 690 | * @priv: LCDC device | 955 | * @priv: LCDC device |
| 691 | * | 956 | * |
| 692 | * Configure all enabled channels and start the LCDC device. All external | 957 | * Configure all enabled channels and start the LCDC device. All external |
| @@ -892,6 +1157,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
| 892 | } | 1157 | } |
| 893 | } | 1158 | } |
| 894 | 1159 | ||
| 1160 | for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) { | ||
| 1161 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k]; | ||
| 1162 | sh_mobile_lcdc_overlay_setup(ovl); | ||
| 1163 | } | ||
| 1164 | |||
| 895 | /* Start the LCDC. */ | 1165 | /* Start the LCDC. */ |
| 896 | __sh_mobile_lcdc_start(priv); | 1166 | __sh_mobile_lcdc_start(priv); |
| 897 | 1167 | ||
| @@ -975,8 +1245,506 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
| 975 | sh_mobile_lcdc_clk_off(priv); | 1245 | sh_mobile_lcdc_clk_off(priv); |
| 976 | } | 1246 | } |
| 977 | 1247 | ||
| 1248 | static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, | ||
| 1249 | struct fb_info *info) | ||
| 1250 | { | ||
| 1251 | if (var->xres > MAX_XRES || var->yres > MAX_YRES) | ||
| 1252 | return -EINVAL; | ||
| 1253 | |||
| 1254 | /* Make sure the virtual resolution is at least as big as the visible | ||
| 1255 | * resolution. | ||
| 1256 | */ | ||
| 1257 | if (var->xres_virtual < var->xres) | ||
| 1258 | var->xres_virtual = var->xres; | ||
| 1259 | if (var->yres_virtual < var->yres) | ||
| 1260 | var->yres_virtual = var->yres; | ||
| 1261 | |||
| 1262 | if (sh_mobile_format_is_fourcc(var)) { | ||
| 1263 | const struct sh_mobile_lcdc_format_info *format; | ||
| 1264 | |||
| 1265 | format = sh_mobile_format_info(var->grayscale); | ||
| 1266 | if (format == NULL) | ||
| 1267 | return -EINVAL; | ||
| 1268 | var->bits_per_pixel = format->bpp; | ||
| 1269 | |||
| 1270 | /* Default to RGB and JPEG color-spaces for RGB and YUV formats | ||
| 1271 | * respectively. | ||
| 1272 | */ | ||
| 1273 | if (!format->yuv) | ||
| 1274 | var->colorspace = V4L2_COLORSPACE_SRGB; | ||
| 1275 | else if (var->colorspace != V4L2_COLORSPACE_REC709) | ||
| 1276 | var->colorspace = V4L2_COLORSPACE_JPEG; | ||
| 1277 | } else { | ||
| 1278 | if (var->bits_per_pixel <= 16) { /* RGB 565 */ | ||
| 1279 | var->bits_per_pixel = 16; | ||
| 1280 | var->red.offset = 11; | ||
| 1281 | var->red.length = 5; | ||
| 1282 | var->green.offset = 5; | ||
| 1283 | var->green.length = 6; | ||
| 1284 | var->blue.offset = 0; | ||
| 1285 | var->blue.length = 5; | ||
| 1286 | var->transp.offset = 0; | ||
| 1287 | var->transp.length = 0; | ||
| 1288 | } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ | ||
| 1289 | var->bits_per_pixel = 24; | ||
| 1290 | var->red.offset = 16; | ||
| 1291 | var->red.length = 8; | ||
| 1292 | var->green.offset = 8; | ||
| 1293 | var->green.length = 8; | ||
| 1294 | var->blue.offset = 0; | ||
| 1295 | var->blue.length = 8; | ||
| 1296 | var->transp.offset = 0; | ||
| 1297 | var->transp.length = 0; | ||
| 1298 | } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ | ||
| 1299 | var->bits_per_pixel = 32; | ||
| 1300 | var->red.offset = 16; | ||
| 1301 | var->red.length = 8; | ||
| 1302 | var->green.offset = 8; | ||
| 1303 | var->green.length = 8; | ||
| 1304 | var->blue.offset = 0; | ||
| 1305 | var->blue.length = 8; | ||
| 1306 | var->transp.offset = 24; | ||
| 1307 | var->transp.length = 8; | ||
| 1308 | } else | ||
| 1309 | return -EINVAL; | ||
| 1310 | |||
| 1311 | var->red.msb_right = 0; | ||
| 1312 | var->green.msb_right = 0; | ||
| 1313 | var->blue.msb_right = 0; | ||
| 1314 | var->transp.msb_right = 0; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | /* Make sure we don't exceed our allocated memory. */ | ||
| 1318 | if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > | ||
| 1319 | info->fix.smem_len) | ||
| 1320 | return -EINVAL; | ||
| 1321 | |||
| 1322 | return 0; | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | /* ----------------------------------------------------------------------------- | ||
| 1326 | * Frame buffer operations - Overlays | ||
| 1327 | */ | ||
| 1328 | |||
| 1329 | static ssize_t | ||
| 1330 | overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 1331 | { | ||
| 1332 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1333 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1334 | |||
| 1335 | return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | static ssize_t | ||
| 1339 | overlay_alpha_store(struct device *dev, struct device_attribute *attr, | ||
| 1340 | const char *buf, size_t count) | ||
| 1341 | { | ||
| 1342 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1343 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1344 | unsigned int alpha; | ||
| 1345 | char *endp; | ||
| 1346 | |||
| 1347 | alpha = simple_strtoul(buf, &endp, 10); | ||
| 1348 | if (isspace(*endp)) | ||
| 1349 | endp++; | ||
| 1350 | |||
| 1351 | if (endp - buf != count) | ||
| 1352 | return -EINVAL; | ||
| 1353 | |||
| 1354 | if (alpha > 255) | ||
| 1355 | return -EINVAL; | ||
| 1356 | |||
| 1357 | if (ovl->alpha != alpha) { | ||
| 1358 | ovl->alpha = alpha; | ||
| 1359 | |||
| 1360 | if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled) | ||
| 1361 | sh_mobile_lcdc_overlay_setup(ovl); | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | return count; | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | static ssize_t | ||
| 1368 | overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 1369 | { | ||
| 1370 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1371 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1372 | |||
| 1373 | return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode); | ||
| 1374 | } | ||
| 1375 | |||
| 1376 | static ssize_t | ||
| 1377 | overlay_mode_store(struct device *dev, struct device_attribute *attr, | ||
| 1378 | const char *buf, size_t count) | ||
| 1379 | { | ||
| 1380 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1381 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1382 | unsigned int mode; | ||
| 1383 | char *endp; | ||
| 1384 | |||
| 1385 | mode = simple_strtoul(buf, &endp, 10); | ||
| 1386 | if (isspace(*endp)) | ||
| 1387 | endp++; | ||
| 1388 | |||
| 1389 | if (endp - buf != count) | ||
| 1390 | return -EINVAL; | ||
| 1391 | |||
| 1392 | if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3) | ||
| 1393 | return -EINVAL; | ||
| 1394 | |||
| 1395 | if (ovl->mode != mode) { | ||
| 1396 | ovl->mode = mode; | ||
| 1397 | |||
| 1398 | if (ovl->enabled) | ||
| 1399 | sh_mobile_lcdc_overlay_setup(ovl); | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | return count; | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | static ssize_t | ||
| 1406 | overlay_position_show(struct device *dev, struct device_attribute *attr, | ||
| 1407 | char *buf) | ||
| 1408 | { | ||
| 1409 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1410 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1411 | |||
| 1412 | return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y); | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | static ssize_t | ||
| 1416 | overlay_position_store(struct device *dev, struct device_attribute *attr, | ||
| 1417 | const char *buf, size_t count) | ||
| 1418 | { | ||
| 1419 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1420 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1421 | char *endp; | ||
| 1422 | int pos_x; | ||
| 1423 | int pos_y; | ||
| 1424 | |||
| 1425 | pos_x = simple_strtol(buf, &endp, 10); | ||
| 1426 | if (*endp != ',') | ||
| 1427 | return -EINVAL; | ||
| 1428 | |||
| 1429 | pos_y = simple_strtol(endp + 1, &endp, 10); | ||
| 1430 | if (isspace(*endp)) | ||
| 1431 | endp++; | ||
| 1432 | |||
| 1433 | if (endp - buf != count) | ||
| 1434 | return -EINVAL; | ||
| 1435 | |||
| 1436 | if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) { | ||
| 1437 | ovl->pos_x = pos_x; | ||
| 1438 | ovl->pos_y = pos_y; | ||
| 1439 | |||
| 1440 | if (ovl->enabled) | ||
| 1441 | sh_mobile_lcdc_overlay_setup(ovl); | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | return count; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | static ssize_t | ||
| 1448 | overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 1449 | { | ||
| 1450 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1451 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1452 | |||
| 1453 | return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3); | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | static ssize_t | ||
| 1457 | overlay_rop3_store(struct device *dev, struct device_attribute *attr, | ||
| 1458 | const char *buf, size_t count) | ||
| 1459 | { | ||
| 1460 | struct fb_info *info = dev_get_drvdata(dev); | ||
| 1461 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1462 | unsigned int rop3; | ||
| 1463 | char *endp; | ||
| 1464 | |||
| 1465 | rop3 = !!simple_strtoul(buf, &endp, 10); | ||
| 1466 | if (isspace(*endp)) | ||
| 1467 | endp++; | ||
| 1468 | |||
| 1469 | if (endp - buf != count) | ||
| 1470 | return -EINVAL; | ||
| 1471 | |||
| 1472 | if (rop3 > 255) | ||
| 1473 | return -EINVAL; | ||
| 1474 | |||
| 1475 | if (ovl->rop3 != rop3) { | ||
| 1476 | ovl->rop3 = rop3; | ||
| 1477 | |||
| 1478 | if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled) | ||
| 1479 | sh_mobile_lcdc_overlay_setup(ovl); | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | return count; | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | static const struct device_attribute overlay_sysfs_attrs[] = { | ||
| 1486 | __ATTR(ovl_alpha, S_IRUGO|S_IWUSR, | ||
| 1487 | overlay_alpha_show, overlay_alpha_store), | ||
| 1488 | __ATTR(ovl_mode, S_IRUGO|S_IWUSR, | ||
| 1489 | overlay_mode_show, overlay_mode_store), | ||
| 1490 | __ATTR(ovl_position, S_IRUGO|S_IWUSR, | ||
| 1491 | overlay_position_show, overlay_position_store), | ||
| 1492 | __ATTR(ovl_rop3, S_IRUGO|S_IWUSR, | ||
| 1493 | overlay_rop3_show, overlay_rop3_store), | ||
| 1494 | }; | ||
| 1495 | |||
| 1496 | static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = { | ||
| 1497 | .id = "SH Mobile LCDC", | ||
| 1498 | .type = FB_TYPE_PACKED_PIXELS, | ||
| 1499 | .visual = FB_VISUAL_TRUECOLOR, | ||
| 1500 | .accel = FB_ACCEL_NONE, | ||
| 1501 | .xpanstep = 0, | ||
| 1502 | .ypanstep = 1, | ||
| 1503 | .ywrapstep = 0, | ||
| 1504 | .capabilities = FB_CAP_FOURCC, | ||
| 1505 | }; | ||
| 1506 | |||
| 1507 | static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var, | ||
| 1508 | struct fb_info *info) | ||
| 1509 | { | ||
| 1510 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1511 | unsigned long base_addr_y; | ||
| 1512 | unsigned long base_addr_c; | ||
| 1513 | unsigned long pan_offset; | ||
| 1514 | unsigned long c_offset; | ||
| 1515 | |||
| 1516 | if (!ovl->format->yuv) | ||
| 1517 | pan_offset = var->yoffset * ovl->pitch | ||
| 1518 | + var->xoffset * (ovl->format->bpp / 8); | ||
| 1519 | else | ||
| 1520 | pan_offset = var->yoffset * ovl->pitch + var->xoffset; | ||
| 1521 | |||
| 1522 | if (pan_offset == ovl->pan_offset) | ||
| 1523 | return 0; /* No change, do nothing */ | ||
| 1524 | |||
| 1525 | /* Set the source address for the next refresh */ | ||
| 1526 | base_addr_y = ovl->dma_handle + pan_offset; | ||
| 1527 | |||
| 1528 | ovl->base_addr_y = base_addr_y; | ||
| 1529 | ovl->base_addr_c = base_addr_y; | ||
| 1530 | |||
| 1531 | if (ovl->format->yuv) { | ||
| 1532 | /* Set Y offset */ | ||
| 1533 | c_offset = var->yoffset * ovl->pitch | ||
| 1534 | * (ovl->format->bpp - 8) / 8; | ||
| 1535 | base_addr_c = ovl->dma_handle | ||
| 1536 | + ovl->xres * ovl->yres_virtual | ||
| 1537 | + c_offset; | ||
| 1538 | /* Set X offset */ | ||
| 1539 | if (ovl->format->fourcc == V4L2_PIX_FMT_NV24) | ||
| 1540 | base_addr_c += 2 * var->xoffset; | ||
| 1541 | else | ||
| 1542 | base_addr_c += var->xoffset; | ||
| 1543 | |||
| 1544 | ovl->base_addr_c = base_addr_c; | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); | ||
| 1548 | lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); | ||
| 1549 | |||
| 1550 | ovl->pan_offset = pan_offset; | ||
| 1551 | |||
| 1552 | return 0; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd, | ||
| 1556 | unsigned long arg) | ||
| 1557 | { | ||
| 1558 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1559 | |||
| 1560 | switch (cmd) { | ||
| 1561 | case FBIO_WAITFORVSYNC: | ||
| 1562 | return sh_mobile_lcdc_wait_for_vsync(ovl->channel); | ||
| 1563 | |||
| 1564 | default: | ||
| 1565 | return -ENOIOCTLCMD; | ||
| 1566 | } | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var, | ||
| 1570 | struct fb_info *info) | ||
| 1571 | { | ||
| 1572 | return __sh_mobile_lcdc_check_var(var, info); | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info) | ||
| 1576 | { | ||
| 1577 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1578 | |||
| 1579 | ovl->format = | ||
| 1580 | sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); | ||
| 1581 | |||
| 1582 | ovl->xres = info->var.xres; | ||
| 1583 | ovl->xres_virtual = info->var.xres_virtual; | ||
| 1584 | ovl->yres = info->var.yres; | ||
| 1585 | ovl->yres_virtual = info->var.yres_virtual; | ||
| 1586 | |||
| 1587 | if (ovl->format->yuv) | ||
| 1588 | ovl->pitch = info->var.xres; | ||
| 1589 | else | ||
| 1590 | ovl->pitch = info->var.xres * ovl->format->bpp / 8; | ||
| 1591 | |||
| 1592 | sh_mobile_lcdc_overlay_setup(ovl); | ||
| 1593 | |||
| 1594 | info->fix.line_length = ovl->pitch; | ||
| 1595 | |||
| 1596 | if (sh_mobile_format_is_fourcc(&info->var)) { | ||
| 1597 | info->fix.type = FB_TYPE_FOURCC; | ||
| 1598 | info->fix.visual = FB_VISUAL_FOURCC; | ||
| 1599 | } else { | ||
| 1600 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
| 1601 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | return 0; | ||
| 1605 | } | ||
| 1606 | |||
| 1607 | /* Overlay blanking. Disable the overlay when blanked. */ | ||
| 1608 | static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info) | ||
| 1609 | { | ||
| 1610 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
| 1611 | |||
| 1612 | ovl->enabled = !blank; | ||
| 1613 | sh_mobile_lcdc_overlay_setup(ovl); | ||
| 1614 | |||
| 1615 | /* Prevent the backlight from receiving a blanking event by returning | ||
| 1616 | * a non-zero value. | ||
| 1617 | */ | ||
| 1618 | return 1; | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | static struct fb_ops sh_mobile_lcdc_overlay_ops = { | ||
| 1622 | .owner = THIS_MODULE, | ||
| 1623 | .fb_read = fb_sys_read, | ||
| 1624 | .fb_write = fb_sys_write, | ||
| 1625 | .fb_fillrect = sys_fillrect, | ||
| 1626 | .fb_copyarea = sys_copyarea, | ||
| 1627 | .fb_imageblit = sys_imageblit, | ||
| 1628 | .fb_blank = sh_mobile_lcdc_overlay_blank, | ||
| 1629 | .fb_pan_display = sh_mobile_lcdc_overlay_pan, | ||
| 1630 | .fb_ioctl = sh_mobile_lcdc_overlay_ioctl, | ||
| 1631 | .fb_check_var = sh_mobile_lcdc_overlay_check_var, | ||
| 1632 | .fb_set_par = sh_mobile_lcdc_overlay_set_par, | ||
| 1633 | }; | ||
| 1634 | |||
| 1635 | static void | ||
| 1636 | sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl) | ||
| 1637 | { | ||
| 1638 | struct fb_info *info = ovl->info; | ||
| 1639 | |||
| 1640 | if (info == NULL || info->dev == NULL) | ||
| 1641 | return; | ||
| 1642 | |||
| 1643 | unregister_framebuffer(ovl->info); | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | static int __devinit | ||
| 1647 | sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl) | ||
| 1648 | { | ||
| 1649 | struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc; | ||
| 1650 | struct fb_info *info = ovl->info; | ||
| 1651 | unsigned int i; | ||
| 1652 | int ret; | ||
| 1653 | |||
| 1654 | if (info == NULL) | ||
| 1655 | return 0; | ||
| 1656 | |||
| 1657 | ret = register_framebuffer(info); | ||
| 1658 | if (ret < 0) | ||
| 1659 | return ret; | ||
| 1660 | |||
| 1661 | dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n", | ||
| 1662 | dev_name(lcdc->dev), ovl->index, info->var.xres, | ||
| 1663 | info->var.yres, info->var.bits_per_pixel); | ||
| 1664 | |||
| 1665 | for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) { | ||
| 1666 | ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]); | ||
| 1667 | if (ret < 0) | ||
| 1668 | return ret; | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | return 0; | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | static void | ||
| 1675 | sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl) | ||
| 1676 | { | ||
| 1677 | struct fb_info *info = ovl->info; | ||
| 1678 | |||
| 1679 | if (info == NULL || info->device == NULL) | ||
| 1680 | return; | ||
| 1681 | |||
| 1682 | framebuffer_release(info); | ||
| 1683 | } | ||
| 1684 | |||
| 1685 | static int __devinit | ||
| 1686 | sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl) | ||
| 1687 | { | ||
| 1688 | struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc; | ||
| 1689 | struct fb_var_screeninfo *var; | ||
| 1690 | struct fb_info *info; | ||
| 1691 | |||
| 1692 | /* Allocate and initialize the frame buffer device. */ | ||
| 1693 | info = framebuffer_alloc(0, priv->dev); | ||
| 1694 | if (info == NULL) { | ||
| 1695 | dev_err(priv->dev, "unable to allocate fb_info\n"); | ||
| 1696 | return -ENOMEM; | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | ovl->info = info; | ||
| 1700 | |||
| 1701 | info->flags = FBINFO_FLAG_DEFAULT; | ||
| 1702 | info->fbops = &sh_mobile_lcdc_overlay_ops; | ||
| 1703 | info->device = priv->dev; | ||
| 1704 | info->screen_base = ovl->fb_mem; | ||
| 1705 | info->par = ovl; | ||
| 1706 | |||
| 1707 | /* Initialize fixed screen information. Restrict pan to 2 lines steps | ||
| 1708 | * for NV12 and NV21. | ||
| 1709 | */ | ||
| 1710 | info->fix = sh_mobile_lcdc_overlay_fix; | ||
| 1711 | snprintf(info->fix.id, sizeof(info->fix.id), | ||
| 1712 | "SH Mobile LCDC Overlay %u", ovl->index); | ||
| 1713 | info->fix.smem_start = ovl->dma_handle; | ||
| 1714 | info->fix.smem_len = ovl->fb_size; | ||
| 1715 | info->fix.line_length = ovl->pitch; | ||
| 1716 | |||
| 1717 | if (ovl->format->yuv) | ||
| 1718 | info->fix.visual = FB_VISUAL_FOURCC; | ||
| 1719 | else | ||
| 1720 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 1721 | |||
| 1722 | if (ovl->format->fourcc == V4L2_PIX_FMT_NV12 || | ||
| 1723 | ovl->format->fourcc == V4L2_PIX_FMT_NV21) | ||
| 1724 | info->fix.ypanstep = 2; | ||
| 1725 | |||
| 1726 | /* Initialize variable screen information. */ | ||
| 1727 | var = &info->var; | ||
| 1728 | memset(var, 0, sizeof(*var)); | ||
| 1729 | var->xres = ovl->xres; | ||
| 1730 | var->yres = ovl->yres; | ||
| 1731 | var->xres_virtual = ovl->xres_virtual; | ||
| 1732 | var->yres_virtual = ovl->yres_virtual; | ||
| 1733 | var->activate = FB_ACTIVATE_NOW; | ||
| 1734 | |||
| 1735 | /* Use the legacy API by default for RGB formats, and the FOURCC API | ||
| 1736 | * for YUV formats. | ||
| 1737 | */ | ||
| 1738 | if (!ovl->format->yuv) | ||
| 1739 | var->bits_per_pixel = ovl->format->bpp; | ||
| 1740 | else | ||
| 1741 | var->grayscale = ovl->format->fourcc; | ||
| 1742 | |||
| 1743 | return sh_mobile_lcdc_overlay_check_var(var, info); | ||
| 1744 | } | ||
| 1745 | |||
| 978 | /* ----------------------------------------------------------------------------- | 1746 | /* ----------------------------------------------------------------------------- |
| 979 | * Frame buffer operations | 1747 | * Frame buffer operations - main frame buffer |
| 980 | */ | 1748 | */ |
| 981 | 1749 | ||
| 982 | static int sh_mobile_lcdc_setcolreg(u_int regno, | 1750 | static int sh_mobile_lcdc_setcolreg(u_int regno, |
| @@ -1003,7 +1771,7 @@ static int sh_mobile_lcdc_setcolreg(u_int regno, | |||
| 1003 | return 0; | 1771 | return 0; |
| 1004 | } | 1772 | } |
| 1005 | 1773 | ||
| 1006 | static struct fb_fix_screeninfo sh_mobile_lcdc_fix = { | 1774 | static const struct fb_fix_screeninfo sh_mobile_lcdc_fix = { |
| 1007 | .id = "SH Mobile LCDC", | 1775 | .id = "SH Mobile LCDC", |
| 1008 | .type = FB_TYPE_PACKED_PIXELS, | 1776 | .type = FB_TYPE_PACKED_PIXELS, |
| 1009 | .visual = FB_VISUAL_TRUECOLOR, | 1777 | .visual = FB_VISUAL_TRUECOLOR, |
| @@ -1035,8 +1803,8 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info, | |||
| 1035 | sh_mobile_lcdc_deferred_io_touch(info); | 1803 | sh_mobile_lcdc_deferred_io_touch(info); |
| 1036 | } | 1804 | } |
| 1037 | 1805 | ||
| 1038 | static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, | 1806 | static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var, |
| 1039 | struct fb_info *info) | 1807 | struct fb_info *info) |
| 1040 | { | 1808 | { |
| 1041 | struct sh_mobile_lcdc_chan *ch = info->par; | 1809 | struct sh_mobile_lcdc_chan *ch = info->par; |
| 1042 | struct sh_mobile_lcdc_priv *priv = ch->lcdc; | 1810 | struct sh_mobile_lcdc_priv *priv = ch->lcdc; |
| @@ -1099,14 +1867,15 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, | |||
| 1099 | return 0; | 1867 | return 0; |
| 1100 | } | 1868 | } |
| 1101 | 1869 | ||
| 1102 | static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, | 1870 | static int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd, |
| 1103 | unsigned long arg) | 1871 | unsigned long arg) |
| 1104 | { | 1872 | { |
| 1873 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
| 1105 | int retval; | 1874 | int retval; |
| 1106 | 1875 | ||
| 1107 | switch (cmd) { | 1876 | switch (cmd) { |
| 1108 | case FBIO_WAITFORVSYNC: | 1877 | case FBIO_WAITFORVSYNC: |
| 1109 | retval = sh_mobile_wait_for_vsync(info->par); | 1878 | retval = sh_mobile_lcdc_wait_for_vsync(ch); |
| 1110 | break; | 1879 | break; |
| 1111 | 1880 | ||
| 1112 | default: | 1881 | default: |
| @@ -1158,7 +1927,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info) | |||
| 1158 | * Locking: both .fb_release() and .fb_open() are called with info->lock held if | 1927 | * Locking: both .fb_release() and .fb_open() are called with info->lock held if |
| 1159 | * user == 1, or with console sem held, if user == 0. | 1928 | * user == 1, or with console sem held, if user == 0. |
| 1160 | */ | 1929 | */ |
| 1161 | static int sh_mobile_release(struct fb_info *info, int user) | 1930 | static int sh_mobile_lcdc_release(struct fb_info *info, int user) |
| 1162 | { | 1931 | { |
| 1163 | struct sh_mobile_lcdc_chan *ch = info->par; | 1932 | struct sh_mobile_lcdc_chan *ch = info->par; |
| 1164 | 1933 | ||
| @@ -1179,7 +1948,7 @@ static int sh_mobile_release(struct fb_info *info, int user) | |||
| 1179 | return 0; | 1948 | return 0; |
| 1180 | } | 1949 | } |
| 1181 | 1950 | ||
| 1182 | static int sh_mobile_open(struct fb_info *info, int user) | 1951 | static int sh_mobile_lcdc_open(struct fb_info *info, int user) |
| 1183 | { | 1952 | { |
| 1184 | struct sh_mobile_lcdc_chan *ch = info->par; | 1953 | struct sh_mobile_lcdc_chan *ch = info->par; |
| 1185 | 1954 | ||
| @@ -1192,7 +1961,8 @@ static int sh_mobile_open(struct fb_info *info, int user) | |||
| 1192 | return 0; | 1961 | return 0; |
| 1193 | } | 1962 | } |
| 1194 | 1963 | ||
| 1195 | static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 1964 | static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, |
| 1965 | struct fb_info *info) | ||
| 1196 | { | 1966 | { |
| 1197 | struct sh_mobile_lcdc_chan *ch = info->par; | 1967 | struct sh_mobile_lcdc_chan *ch = info->par; |
| 1198 | struct sh_mobile_lcdc_priv *p = ch->lcdc; | 1968 | struct sh_mobile_lcdc_priv *p = ch->lcdc; |
| @@ -1200,9 +1970,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
| 1200 | unsigned int best_xres = 0; | 1970 | unsigned int best_xres = 0; |
| 1201 | unsigned int best_yres = 0; | 1971 | unsigned int best_yres = 0; |
| 1202 | unsigned int i; | 1972 | unsigned int i; |
| 1203 | 1973 | int ret; | |
| 1204 | if (var->xres > MAX_XRES || var->yres > MAX_YRES) | ||
| 1205 | return -EINVAL; | ||
| 1206 | 1974 | ||
| 1207 | /* If board code provides us with a list of available modes, make sure | 1975 | /* If board code provides us with a list of available modes, make sure |
| 1208 | * we use one of them. Find the mode closest to the requested one. The | 1976 | * we use one of them. Find the mode closest to the requested one. The |
| @@ -1237,73 +2005,9 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
| 1237 | var->yres = best_yres; | 2005 | var->yres = best_yres; |
| 1238 | } | 2006 | } |
| 1239 | 2007 | ||
| 1240 | /* Make sure the virtual resolution is at least as big as the visible | 2008 | ret = __sh_mobile_lcdc_check_var(var, info); |
| 1241 | * resolution. | 2009 | if (ret < 0) |
| 1242 | */ | 2010 | return ret; |
| 1243 | if (var->xres_virtual < var->xres) | ||
| 1244 | var->xres_virtual = var->xres; | ||
| 1245 | if (var->yres_virtual < var->yres) | ||
| 1246 | var->yres_virtual = var->yres; | ||
| 1247 | |||
| 1248 | if (sh_mobile_format_is_fourcc(var)) { | ||
| 1249 | const struct sh_mobile_lcdc_format_info *format; | ||
| 1250 | |||
| 1251 | format = sh_mobile_format_info(var->grayscale); | ||
| 1252 | if (format == NULL) | ||
| 1253 | return -EINVAL; | ||
| 1254 | var->bits_per_pixel = format->bpp; | ||
| 1255 | |||
| 1256 | /* Default to RGB and JPEG color-spaces for RGB and YUV formats | ||
| 1257 | * respectively. | ||
| 1258 | */ | ||
| 1259 | if (!format->yuv) | ||
| 1260 | var->colorspace = V4L2_COLORSPACE_SRGB; | ||
| 1261 | else if (var->colorspace != V4L2_COLORSPACE_REC709) | ||
| 1262 | var->colorspace = V4L2_COLORSPACE_JPEG; | ||
| 1263 | } else { | ||
| 1264 | if (var->bits_per_pixel <= 16) { /* RGB 565 */ | ||
| 1265 | var->bits_per_pixel = 16; | ||
| 1266 | var->red.offset = 11; | ||
| 1267 | var->red.length = 5; | ||
| 1268 | var->green.offset = 5; | ||
| 1269 | var->green.length = 6; | ||
| 1270 | var->blue.offset = 0; | ||
| 1271 | var->blue.length = 5; | ||
| 1272 | var->transp.offset = 0; | ||
| 1273 | var->transp.length = 0; | ||
| 1274 | } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ | ||
| 1275 | var->bits_per_pixel = 24; | ||
| 1276 | var->red.offset = 16; | ||
| 1277 | var->red.length = 8; | ||
| 1278 | var->green.offset = 8; | ||
| 1279 | var->green.length = 8; | ||
| 1280 | var->blue.offset = 0; | ||
| 1281 | var->blue.length = 8; | ||
| 1282 | var->transp.offset = 0; | ||
| 1283 | var->transp.length = 0; | ||
| 1284 | } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ | ||
| 1285 | var->bits_per_pixel = 32; | ||
| 1286 | var->red.offset = 16; | ||
| 1287 | var->red.length = 8; | ||
| 1288 | var->green.offset = 8; | ||
| 1289 | var->green.length = 8; | ||
| 1290 | var->blue.offset = 0; | ||
| 1291 | var->blue.length = 8; | ||
| 1292 | var->transp.offset = 24; | ||
| 1293 | var->transp.length = 8; | ||
| 1294 | } else | ||
| 1295 | return -EINVAL; | ||
| 1296 | |||
| 1297 | var->red.msb_right = 0; | ||
| 1298 | var->green.msb_right = 0; | ||
| 1299 | var->blue.msb_right = 0; | ||
| 1300 | var->transp.msb_right = 0; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | /* Make sure we don't exceed our allocated memory. */ | ||
| 1304 | if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > | ||
| 1305 | info->fix.smem_len) | ||
| 1306 | return -EINVAL; | ||
| 1307 | 2011 | ||
| 1308 | /* only accept the forced_fourcc for dual channel configurations */ | 2012 | /* only accept the forced_fourcc for dual channel configurations */ |
| 1309 | if (p->forced_fourcc && | 2013 | if (p->forced_fourcc && |
| @@ -1313,7 +2017,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
| 1313 | return 0; | 2017 | return 0; |
| 1314 | } | 2018 | } |
| 1315 | 2019 | ||
| 1316 | static int sh_mobile_set_par(struct fb_info *info) | 2020 | static int sh_mobile_lcdc_set_par(struct fb_info *info) |
| 1317 | { | 2021 | { |
| 1318 | struct sh_mobile_lcdc_chan *ch = info->par; | 2022 | struct sh_mobile_lcdc_chan *ch = info->par; |
| 1319 | int ret; | 2023 | int ret; |
| @@ -1383,8 +2087,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info) | |||
| 1383 | * mode will reenable the clocks and update the screen in time, | 2087 | * mode will reenable the clocks and update the screen in time, |
| 1384 | * so it does not need this. */ | 2088 | * so it does not need this. */ |
| 1385 | if (!info->fbdefio) { | 2089 | if (!info->fbdefio) { |
| 1386 | sh_mobile_wait_for_vsync(ch); | 2090 | sh_mobile_lcdc_wait_for_vsync(ch); |
| 1387 | sh_mobile_wait_for_vsync(ch); | 2091 | sh_mobile_lcdc_wait_for_vsync(ch); |
| 1388 | } | 2092 | } |
| 1389 | sh_mobile_lcdc_clk_off(p); | 2093 | sh_mobile_lcdc_clk_off(p); |
| 1390 | } | 2094 | } |
| @@ -1402,12 +2106,12 @@ static struct fb_ops sh_mobile_lcdc_ops = { | |||
| 1402 | .fb_copyarea = sh_mobile_lcdc_copyarea, | 2106 | .fb_copyarea = sh_mobile_lcdc_copyarea, |
| 1403 | .fb_imageblit = sh_mobile_lcdc_imageblit, | 2107 | .fb_imageblit = sh_mobile_lcdc_imageblit, |
| 1404 | .fb_blank = sh_mobile_lcdc_blank, | 2108 | .fb_blank = sh_mobile_lcdc_blank, |
| 1405 | .fb_pan_display = sh_mobile_fb_pan_display, | 2109 | .fb_pan_display = sh_mobile_lcdc_pan, |
| 1406 | .fb_ioctl = sh_mobile_ioctl, | 2110 | .fb_ioctl = sh_mobile_lcdc_ioctl, |
| 1407 | .fb_open = sh_mobile_open, | 2111 | .fb_open = sh_mobile_lcdc_open, |
| 1408 | .fb_release = sh_mobile_release, | 2112 | .fb_release = sh_mobile_lcdc_release, |
| 1409 | .fb_check_var = sh_mobile_check_var, | 2113 | .fb_check_var = sh_mobile_lcdc_check_var, |
| 1410 | .fb_set_par = sh_mobile_set_par, | 2114 | .fb_set_par = sh_mobile_lcdc_set_par, |
| 1411 | }; | 2115 | }; |
| 1412 | 2116 | ||
| 1413 | static void | 2117 | static void |
| @@ -1537,7 +2241,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, | |||
| 1537 | else | 2241 | else |
| 1538 | var->grayscale = ch->format->fourcc; | 2242 | var->grayscale = ch->format->fourcc; |
| 1539 | 2243 | ||
| 1540 | ret = sh_mobile_check_var(var, info); | 2244 | ret = sh_mobile_lcdc_check_var(var, info); |
| 1541 | if (ret) | 2245 | if (ret) |
| 1542 | return ret; | 2246 | return ret; |
| 1543 | 2247 | ||
| @@ -1712,15 +2416,27 @@ static const struct fb_videomode default_720p __devinitconst = { | |||
| 1712 | static int sh_mobile_lcdc_remove(struct platform_device *pdev) | 2416 | static int sh_mobile_lcdc_remove(struct platform_device *pdev) |
| 1713 | { | 2417 | { |
| 1714 | struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); | 2418 | struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); |
| 1715 | int i; | 2419 | unsigned int i; |
| 1716 | 2420 | ||
| 1717 | fb_unregister_client(&priv->notifier); | 2421 | fb_unregister_client(&priv->notifier); |
| 1718 | 2422 | ||
| 2423 | for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) | ||
| 2424 | sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]); | ||
| 1719 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) | 2425 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) |
| 1720 | sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); | 2426 | sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); |
| 1721 | 2427 | ||
| 1722 | sh_mobile_lcdc_stop(priv); | 2428 | sh_mobile_lcdc_stop(priv); |
| 1723 | 2429 | ||
| 2430 | for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) { | ||
| 2431 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; | ||
| 2432 | |||
| 2433 | sh_mobile_lcdc_overlay_fb_cleanup(ovl); | ||
| 2434 | |||
| 2435 | if (ovl->fb_mem) | ||
| 2436 | dma_free_coherent(&pdev->dev, ovl->fb_size, | ||
| 2437 | ovl->fb_mem, ovl->dma_handle); | ||
| 2438 | } | ||
| 2439 | |||
| 1724 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { | 2440 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { |
| 1725 | struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; | 2441 | struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; |
| 1726 | 2442 | ||
| @@ -1796,6 +2512,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan * | |||
| 1796 | } | 2512 | } |
| 1797 | 2513 | ||
| 1798 | static int __devinit | 2514 | static int __devinit |
| 2515 | sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, | ||
| 2516 | struct sh_mobile_lcdc_overlay *ovl) | ||
| 2517 | { | ||
| 2518 | const struct sh_mobile_lcdc_format_info *format; | ||
| 2519 | int ret; | ||
| 2520 | |||
| 2521 | if (ovl->cfg->fourcc == 0) | ||
| 2522 | return 0; | ||
| 2523 | |||
| 2524 | /* Validate the format. */ | ||
| 2525 | format = sh_mobile_format_info(ovl->cfg->fourcc); | ||
| 2526 | if (format == NULL) { | ||
| 2527 | dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc); | ||
| 2528 | return -EINVAL; | ||
| 2529 | } | ||
| 2530 | |||
| 2531 | ovl->enabled = false; | ||
| 2532 | ovl->mode = LCDC_OVERLAY_BLEND; | ||
| 2533 | ovl->alpha = 255; | ||
| 2534 | ovl->rop3 = 0; | ||
| 2535 | ovl->pos_x = 0; | ||
| 2536 | ovl->pos_y = 0; | ||
| 2537 | |||
| 2538 | /* The default Y virtual resolution is twice the panel size to allow for | ||
| 2539 | * double-buffering. | ||
| 2540 | */ | ||
| 2541 | ovl->format = format; | ||
| 2542 | ovl->xres = ovl->cfg->max_xres; | ||
| 2543 | ovl->xres_virtual = ovl->xres; | ||
| 2544 | ovl->yres = ovl->cfg->max_yres; | ||
| 2545 | ovl->yres_virtual = ovl->yres * 2; | ||
| 2546 | |||
| 2547 | if (!format->yuv) | ||
| 2548 | ovl->pitch = ovl->xres * format->bpp / 8; | ||
| 2549 | else | ||
| 2550 | ovl->pitch = ovl->xres; | ||
| 2551 | |||
| 2552 | /* Allocate frame buffer memory. */ | ||
| 2553 | ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres | ||
| 2554 | * format->bpp / 8 * 2; | ||
| 2555 | ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size, | ||
| 2556 | &ovl->dma_handle, GFP_KERNEL); | ||
| 2557 | if (!ovl->fb_mem) { | ||
| 2558 | dev_err(priv->dev, "unable to allocate buffer\n"); | ||
| 2559 | return -ENOMEM; | ||
| 2560 | } | ||
| 2561 | |||
| 2562 | ret = sh_mobile_lcdc_overlay_fb_init(ovl); | ||
| 2563 | if (ret < 0) | ||
| 2564 | return ret; | ||
| 2565 | |||
| 2566 | return 0; | ||
| 2567 | } | ||
| 2568 | |||
| 2569 | static int __devinit | ||
| 1799 | sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, | 2570 | sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, |
| 1800 | struct sh_mobile_lcdc_chan *ch) | 2571 | struct sh_mobile_lcdc_chan *ch) |
| 1801 | { | 2572 | { |
| @@ -2003,6 +2774,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
| 2003 | goto err1; | 2774 | goto err1; |
| 2004 | } | 2775 | } |
| 2005 | 2776 | ||
| 2777 | for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { | ||
| 2778 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; | ||
| 2779 | |||
| 2780 | ovl->cfg = &pdata->overlays[i]; | ||
| 2781 | ovl->channel = &priv->ch[0]; | ||
| 2782 | |||
| 2783 | error = sh_mobile_lcdc_overlay_init(priv, ovl); | ||
| 2784 | if (error) | ||
| 2785 | goto err1; | ||
| 2786 | } | ||
| 2787 | |||
| 2006 | error = sh_mobile_lcdc_start(priv); | 2788 | error = sh_mobile_lcdc_start(priv); |
| 2007 | if (error) { | 2789 | if (error) { |
| 2008 | dev_err(&pdev->dev, "unable to start hardware\n"); | 2790 | dev_err(&pdev->dev, "unable to start hardware\n"); |
| @@ -2017,6 +2799,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
| 2017 | goto err1; | 2799 | goto err1; |
| 2018 | } | 2800 | } |
| 2019 | 2801 | ||
| 2802 | for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { | ||
| 2803 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; | ||
| 2804 | |||
| 2805 | error = sh_mobile_lcdc_overlay_fb_register(ovl); | ||
| 2806 | if (error) | ||
| 2807 | goto err1; | ||
| 2808 | } | ||
| 2809 | |||
| 2020 | /* Failure ignored */ | 2810 | /* Failure ignored */ |
| 2021 | priv->notifier.notifier_call = sh_mobile_lcdc_notify; | 2811 | priv->notifier.notifier_call = sh_mobile_lcdc_notify; |
| 2022 | fb_register_client(&priv->notifier); | 2812 | fb_register_client(&priv->notifier); |
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h index 7571b27a0ba1..ff43ffc1aab2 100644 --- a/include/video/sh_mobile_lcdc.h +++ b/include/video/sh_mobile_lcdc.h | |||
| @@ -166,6 +166,12 @@ struct sh_mobile_lcdc_bl_info { | |||
| 166 | int (*get_brightness)(void); | 166 | int (*get_brightness)(void); |
| 167 | }; | 167 | }; |
| 168 | 168 | ||
| 169 | struct sh_mobile_lcdc_overlay_cfg { | ||
| 170 | int fourcc; | ||
| 171 | unsigned int max_xres; | ||
| 172 | unsigned int max_yres; | ||
| 173 | }; | ||
| 174 | |||
| 169 | struct sh_mobile_lcdc_chan_cfg { | 175 | struct sh_mobile_lcdc_chan_cfg { |
| 170 | int chan; | 176 | int chan; |
| 171 | int fourcc; | 177 | int fourcc; |
| @@ -186,6 +192,7 @@ struct sh_mobile_lcdc_chan_cfg { | |||
| 186 | struct sh_mobile_lcdc_info { | 192 | struct sh_mobile_lcdc_info { |
| 187 | int clock_source; | 193 | int clock_source; |
| 188 | struct sh_mobile_lcdc_chan_cfg ch[2]; | 194 | struct sh_mobile_lcdc_chan_cfg ch[2]; |
| 195 | struct sh_mobile_lcdc_overlay_cfg overlays[4]; | ||
| 189 | struct sh_mobile_meram_info *meram_dev; | 196 | struct sh_mobile_meram_info *meram_dev; |
| 190 | }; | 197 | }; |
| 191 | 198 | ||
