diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-12-12 12:43:16 -0500 |
---|---|---|
committer | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2012-06-20 04:02:54 -0400 |
commit | c5deac3c9b2284a64326e8799dfe7416bc619c02 (patch) | |
tree | 4279b885aba3bfd0554ecc026df5254d3c9c6f4d /drivers | |
parent | d7ad33421863308fe7ddf865737d4d83b64e5b81 (diff) |
fbdev: sh_mobile_lcdc: Implement overlays support
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 932 |
1 files changed, 860 insertions, 72 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 799f87171e04..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 | { |
@@ -685,6 +860,96 @@ 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 start the LCDC | 954 | * __sh_mobile_lcdc_start - Configure and start the LCDC |
690 | * @priv: LCDC device | 955 | * @priv: LCDC device |
@@ -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, |
@@ -1202,9 +1970,7 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, | |||
1202 | unsigned int best_xres = 0; | 1970 | unsigned int best_xres = 0; |
1203 | unsigned int best_yres = 0; | 1971 | unsigned int best_yres = 0; |
1204 | unsigned int i; | 1972 | unsigned int i; |
1205 | 1973 | int ret; | |
1206 | if (var->xres > MAX_XRES || var->yres > MAX_YRES) | ||
1207 | return -EINVAL; | ||
1208 | 1974 | ||
1209 | /* 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 |
1210 | * 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 |
@@ -1239,73 +2005,9 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, | |||
1239 | var->yres = best_yres; | 2005 | var->yres = best_yres; |
1240 | } | 2006 | } |
1241 | 2007 | ||
1242 | /* Make sure the virtual resolution is at least as big as the visible | 2008 | ret = __sh_mobile_lcdc_check_var(var, info); |
1243 | * resolution. | 2009 | if (ret < 0) |
1244 | */ | 2010 | return ret; |
1245 | if (var->xres_virtual < var->xres) | ||
1246 | var->xres_virtual = var->xres; | ||
1247 | if (var->yres_virtual < var->yres) | ||
1248 | var->yres_virtual = var->yres; | ||
1249 | |||
1250 | if (sh_mobile_format_is_fourcc(var)) { | ||
1251 | const struct sh_mobile_lcdc_format_info *format; | ||
1252 | |||
1253 | format = sh_mobile_format_info(var->grayscale); | ||
1254 | if (format == NULL) | ||
1255 | return -EINVAL; | ||
1256 | var->bits_per_pixel = format->bpp; | ||
1257 | |||
1258 | /* Default to RGB and JPEG color-spaces for RGB and YUV formats | ||
1259 | * respectively. | ||
1260 | */ | ||
1261 | if (!format->yuv) | ||
1262 | var->colorspace = V4L2_COLORSPACE_SRGB; | ||
1263 | else if (var->colorspace != V4L2_COLORSPACE_REC709) | ||
1264 | var->colorspace = V4L2_COLORSPACE_JPEG; | ||
1265 | } else { | ||
1266 | if (var->bits_per_pixel <= 16) { /* RGB 565 */ | ||
1267 | var->bits_per_pixel = 16; | ||
1268 | var->red.offset = 11; | ||
1269 | var->red.length = 5; | ||
1270 | var->green.offset = 5; | ||
1271 | var->green.length = 6; | ||
1272 | var->blue.offset = 0; | ||
1273 | var->blue.length = 5; | ||
1274 | var->transp.offset = 0; | ||
1275 | var->transp.length = 0; | ||
1276 | } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ | ||
1277 | var->bits_per_pixel = 24; | ||
1278 | var->red.offset = 16; | ||
1279 | var->red.length = 8; | ||
1280 | var->green.offset = 8; | ||
1281 | var->green.length = 8; | ||
1282 | var->blue.offset = 0; | ||
1283 | var->blue.length = 8; | ||
1284 | var->transp.offset = 0; | ||
1285 | var->transp.length = 0; | ||
1286 | } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ | ||
1287 | var->bits_per_pixel = 32; | ||
1288 | var->red.offset = 16; | ||
1289 | var->red.length = 8; | ||
1290 | var->green.offset = 8; | ||
1291 | var->green.length = 8; | ||
1292 | var->blue.offset = 0; | ||
1293 | var->blue.length = 8; | ||
1294 | var->transp.offset = 24; | ||
1295 | var->transp.length = 8; | ||
1296 | } else | ||
1297 | return -EINVAL; | ||
1298 | |||
1299 | var->red.msb_right = 0; | ||
1300 | var->green.msb_right = 0; | ||
1301 | var->blue.msb_right = 0; | ||
1302 | var->transp.msb_right = 0; | ||
1303 | } | ||
1304 | |||
1305 | /* Make sure we don't exceed our allocated memory. */ | ||
1306 | if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > | ||
1307 | info->fix.smem_len) | ||
1308 | return -EINVAL; | ||
1309 | 2011 | ||
1310 | /* only accept the forced_fourcc for dual channel configurations */ | 2012 | /* only accept the forced_fourcc for dual channel configurations */ |
1311 | if (p->forced_fourcc && | 2013 | if (p->forced_fourcc && |
@@ -1714,15 +2416,27 @@ static const struct fb_videomode default_720p __devinitconst = { | |||
1714 | static int sh_mobile_lcdc_remove(struct platform_device *pdev) | 2416 | static int sh_mobile_lcdc_remove(struct platform_device *pdev) |
1715 | { | 2417 | { |
1716 | struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); | 2418 | struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); |
1717 | int i; | 2419 | unsigned int i; |
1718 | 2420 | ||
1719 | fb_unregister_client(&priv->notifier); | 2421 | fb_unregister_client(&priv->notifier); |
1720 | 2422 | ||
2423 | for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) | ||
2424 | sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]); | ||
1721 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) | 2425 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) |
1722 | sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); | 2426 | sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); |
1723 | 2427 | ||
1724 | sh_mobile_lcdc_stop(priv); | 2428 | sh_mobile_lcdc_stop(priv); |
1725 | 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 | |||
1726 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { | 2440 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { |
1727 | struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; | 2441 | struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; |
1728 | 2442 | ||
@@ -1798,6 +2512,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan * | |||
1798 | } | 2512 | } |
1799 | 2513 | ||
1800 | 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 | ||
1801 | sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, | 2570 | sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, |
1802 | struct sh_mobile_lcdc_chan *ch) | 2571 | struct sh_mobile_lcdc_chan *ch) |
1803 | { | 2572 | { |
@@ -2005,6 +2774,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
2005 | goto err1; | 2774 | goto err1; |
2006 | } | 2775 | } |
2007 | 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 | |||
2008 | error = sh_mobile_lcdc_start(priv); | 2788 | error = sh_mobile_lcdc_start(priv); |
2009 | if (error) { | 2789 | if (error) { |
2010 | dev_err(&pdev->dev, "unable to start hardware\n"); | 2790 | dev_err(&pdev->dev, "unable to start hardware\n"); |
@@ -2019,6 +2799,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
2019 | goto err1; | 2799 | goto err1; |
2020 | } | 2800 | } |
2021 | 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 | |||
2022 | /* Failure ignored */ | 2810 | /* Failure ignored */ |
2023 | priv->notifier.notifier_call = sh_mobile_lcdc_notify; | 2811 | priv->notifier.notifier_call = sh_mobile_lcdc_notify; |
2024 | fb_register_client(&priv->notifier); | 2812 | fb_register_client(&priv->notifier); |