diff options
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 1117 |
1 files changed, 956 insertions, 161 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index e672698bd820..699487c287b2 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_y_offset: Panning linear offset in bytes (luma component) | ||
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_y_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->dma_handle | ||
877 | + ovl->xres_virtual * 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 |
@@ -839,27 +1104,25 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
839 | /* Compute frame buffer base address and pitch for each channel. */ | 1104 | /* Compute frame buffer base address and pitch for each channel. */ |
840 | for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { | 1105 | for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { |
841 | int pixelformat; | 1106 | int pixelformat; |
842 | void *meram; | 1107 | void *cache; |
843 | 1108 | ||
844 | ch = &priv->ch[k]; | 1109 | ch = &priv->ch[k]; |
845 | if (!ch->enabled) | 1110 | if (!ch->enabled) |
846 | continue; | 1111 | continue; |
847 | 1112 | ||
848 | ch->base_addr_y = ch->dma_handle; | 1113 | ch->base_addr_y = ch->dma_handle; |
849 | ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual; | 1114 | ch->base_addr_c = ch->dma_handle |
1115 | + ch->xres_virtual * ch->yres_virtual; | ||
850 | ch->line_size = ch->pitch; | 1116 | ch->line_size = ch->pitch; |
851 | 1117 | ||
852 | /* Enable MERAM if possible. */ | 1118 | /* Enable MERAM if possible. */ |
853 | if (mdev == NULL || mdev->ops == NULL || | 1119 | if (mdev == NULL || ch->cfg->meram_cfg == NULL) |
854 | ch->cfg->meram_cfg == NULL) | ||
855 | continue; | 1120 | continue; |
856 | 1121 | ||
857 | /* we need to de-init configured ICBs before we can | 1122 | /* Free the allocated MERAM cache. */ |
858 | * re-initialize them. | 1123 | if (ch->cache) { |
859 | */ | 1124 | sh_mobile_meram_cache_free(mdev, ch->cache); |
860 | if (ch->meram) { | 1125 | ch->cache = NULL; |
861 | mdev->ops->meram_unregister(mdev, ch->meram); | ||
862 | ch->meram = NULL; | ||
863 | } | 1126 | } |
864 | 1127 | ||
865 | switch (ch->format->fourcc) { | 1128 | switch (ch->format->fourcc) { |
@@ -881,17 +1144,22 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
881 | break; | 1144 | break; |
882 | } | 1145 | } |
883 | 1146 | ||
884 | meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg, | 1147 | cache = sh_mobile_meram_cache_alloc(mdev, ch->cfg->meram_cfg, |
885 | ch->pitch, ch->yres, pixelformat, | 1148 | ch->pitch, ch->yres, pixelformat, |
886 | &ch->line_size); | 1149 | &ch->line_size); |
887 | if (!IS_ERR(meram)) { | 1150 | if (!IS_ERR(cache)) { |
888 | mdev->ops->meram_update(mdev, meram, | 1151 | sh_mobile_meram_cache_update(mdev, cache, |
889 | ch->base_addr_y, ch->base_addr_c, | 1152 | ch->base_addr_y, ch->base_addr_c, |
890 | &ch->base_addr_y, &ch->base_addr_c); | 1153 | &ch->base_addr_y, &ch->base_addr_c); |
891 | ch->meram = meram; | 1154 | ch->cache = cache; |
892 | } | 1155 | } |
893 | } | 1156 | } |
894 | 1157 | ||
1158 | for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) { | ||
1159 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k]; | ||
1160 | sh_mobile_lcdc_overlay_setup(ovl); | ||
1161 | } | ||
1162 | |||
895 | /* Start the LCDC. */ | 1163 | /* Start the LCDC. */ |
896 | __sh_mobile_lcdc_start(priv); | 1164 | __sh_mobile_lcdc_start(priv); |
897 | 1165 | ||
@@ -953,12 +1221,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
953 | 1221 | ||
954 | sh_mobile_lcdc_display_off(ch); | 1222 | sh_mobile_lcdc_display_off(ch); |
955 | 1223 | ||
956 | /* disable the meram */ | 1224 | /* Free the MERAM cache. */ |
957 | if (ch->meram) { | 1225 | if (ch->cache) { |
958 | struct sh_mobile_meram_info *mdev; | 1226 | sh_mobile_meram_cache_free(priv->meram_dev, ch->cache); |
959 | mdev = priv->meram_dev; | 1227 | ch->cache = 0; |
960 | mdev->ops->meram_unregister(mdev, ch->meram); | ||
961 | ch->meram = 0; | ||
962 | } | 1228 | } |
963 | 1229 | ||
964 | } | 1230 | } |
@@ -975,8 +1241,511 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
975 | sh_mobile_lcdc_clk_off(priv); | 1241 | sh_mobile_lcdc_clk_off(priv); |
976 | } | 1242 | } |
977 | 1243 | ||
1244 | static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, | ||
1245 | struct fb_info *info) | ||
1246 | { | ||
1247 | if (var->xres > MAX_XRES || var->yres > MAX_YRES) | ||
1248 | return -EINVAL; | ||
1249 | |||
1250 | /* Make sure the virtual resolution is at least as big as the visible | ||
1251 | * resolution. | ||
1252 | */ | ||
1253 | if (var->xres_virtual < var->xres) | ||
1254 | var->xres_virtual = var->xres; | ||
1255 | if (var->yres_virtual < var->yres) | ||
1256 | var->yres_virtual = var->yres; | ||
1257 | |||
1258 | if (sh_mobile_format_is_fourcc(var)) { | ||
1259 | const struct sh_mobile_lcdc_format_info *format; | ||
1260 | |||
1261 | format = sh_mobile_format_info(var->grayscale); | ||
1262 | if (format == NULL) | ||
1263 | return -EINVAL; | ||
1264 | var->bits_per_pixel = format->bpp; | ||
1265 | |||
1266 | /* Default to RGB and JPEG color-spaces for RGB and YUV formats | ||
1267 | * respectively. | ||
1268 | */ | ||
1269 | if (!format->yuv) | ||
1270 | var->colorspace = V4L2_COLORSPACE_SRGB; | ||
1271 | else if (var->colorspace != V4L2_COLORSPACE_REC709) | ||
1272 | var->colorspace = V4L2_COLORSPACE_JPEG; | ||
1273 | } else { | ||
1274 | if (var->bits_per_pixel <= 16) { /* RGB 565 */ | ||
1275 | var->bits_per_pixel = 16; | ||
1276 | var->red.offset = 11; | ||
1277 | var->red.length = 5; | ||
1278 | var->green.offset = 5; | ||
1279 | var->green.length = 6; | ||
1280 | var->blue.offset = 0; | ||
1281 | var->blue.length = 5; | ||
1282 | var->transp.offset = 0; | ||
1283 | var->transp.length = 0; | ||
1284 | } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ | ||
1285 | var->bits_per_pixel = 24; | ||
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 = 0; | ||
1293 | var->transp.length = 0; | ||
1294 | } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ | ||
1295 | var->bits_per_pixel = 32; | ||
1296 | var->red.offset = 16; | ||
1297 | var->red.length = 8; | ||
1298 | var->green.offset = 8; | ||
1299 | var->green.length = 8; | ||
1300 | var->blue.offset = 0; | ||
1301 | var->blue.length = 8; | ||
1302 | var->transp.offset = 24; | ||
1303 | var->transp.length = 8; | ||
1304 | } else | ||
1305 | return -EINVAL; | ||
1306 | |||
1307 | var->red.msb_right = 0; | ||
1308 | var->green.msb_right = 0; | ||
1309 | var->blue.msb_right = 0; | ||
1310 | var->transp.msb_right = 0; | ||
1311 | } | ||
1312 | |||
1313 | /* Make sure we don't exceed our allocated memory. */ | ||
1314 | if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > | ||
1315 | info->fix.smem_len) | ||
1316 | return -EINVAL; | ||
1317 | |||
1318 | return 0; | ||
1319 | } | ||
1320 | |||
978 | /* ----------------------------------------------------------------------------- | 1321 | /* ----------------------------------------------------------------------------- |
979 | * Frame buffer operations | 1322 | * Frame buffer operations - Overlays |
1323 | */ | ||
1324 | |||
1325 | static ssize_t | ||
1326 | overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
1327 | { | ||
1328 | struct fb_info *info = dev_get_drvdata(dev); | ||
1329 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1330 | |||
1331 | return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha); | ||
1332 | } | ||
1333 | |||
1334 | static ssize_t | ||
1335 | overlay_alpha_store(struct device *dev, struct device_attribute *attr, | ||
1336 | const char *buf, size_t count) | ||
1337 | { | ||
1338 | struct fb_info *info = dev_get_drvdata(dev); | ||
1339 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1340 | unsigned int alpha; | ||
1341 | char *endp; | ||
1342 | |||
1343 | alpha = simple_strtoul(buf, &endp, 10); | ||
1344 | if (isspace(*endp)) | ||
1345 | endp++; | ||
1346 | |||
1347 | if (endp - buf != count) | ||
1348 | return -EINVAL; | ||
1349 | |||
1350 | if (alpha > 255) | ||
1351 | return -EINVAL; | ||
1352 | |||
1353 | if (ovl->alpha != alpha) { | ||
1354 | ovl->alpha = alpha; | ||
1355 | |||
1356 | if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled) | ||
1357 | sh_mobile_lcdc_overlay_setup(ovl); | ||
1358 | } | ||
1359 | |||
1360 | return count; | ||
1361 | } | ||
1362 | |||
1363 | static ssize_t | ||
1364 | overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
1365 | { | ||
1366 | struct fb_info *info = dev_get_drvdata(dev); | ||
1367 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1368 | |||
1369 | return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode); | ||
1370 | } | ||
1371 | |||
1372 | static ssize_t | ||
1373 | overlay_mode_store(struct device *dev, struct device_attribute *attr, | ||
1374 | const char *buf, size_t count) | ||
1375 | { | ||
1376 | struct fb_info *info = dev_get_drvdata(dev); | ||
1377 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1378 | unsigned int mode; | ||
1379 | char *endp; | ||
1380 | |||
1381 | mode = simple_strtoul(buf, &endp, 10); | ||
1382 | if (isspace(*endp)) | ||
1383 | endp++; | ||
1384 | |||
1385 | if (endp - buf != count) | ||
1386 | return -EINVAL; | ||
1387 | |||
1388 | if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3) | ||
1389 | return -EINVAL; | ||
1390 | |||
1391 | if (ovl->mode != mode) { | ||
1392 | ovl->mode = mode; | ||
1393 | |||
1394 | if (ovl->enabled) | ||
1395 | sh_mobile_lcdc_overlay_setup(ovl); | ||
1396 | } | ||
1397 | |||
1398 | return count; | ||
1399 | } | ||
1400 | |||
1401 | static ssize_t | ||
1402 | overlay_position_show(struct device *dev, struct device_attribute *attr, | ||
1403 | char *buf) | ||
1404 | { | ||
1405 | struct fb_info *info = dev_get_drvdata(dev); | ||
1406 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1407 | |||
1408 | return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y); | ||
1409 | } | ||
1410 | |||
1411 | static ssize_t | ||
1412 | overlay_position_store(struct device *dev, struct device_attribute *attr, | ||
1413 | const char *buf, size_t count) | ||
1414 | { | ||
1415 | struct fb_info *info = dev_get_drvdata(dev); | ||
1416 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1417 | char *endp; | ||
1418 | int pos_x; | ||
1419 | int pos_y; | ||
1420 | |||
1421 | pos_x = simple_strtol(buf, &endp, 10); | ||
1422 | if (*endp != ',') | ||
1423 | return -EINVAL; | ||
1424 | |||
1425 | pos_y = simple_strtol(endp + 1, &endp, 10); | ||
1426 | if (isspace(*endp)) | ||
1427 | endp++; | ||
1428 | |||
1429 | if (endp - buf != count) | ||
1430 | return -EINVAL; | ||
1431 | |||
1432 | if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) { | ||
1433 | ovl->pos_x = pos_x; | ||
1434 | ovl->pos_y = pos_y; | ||
1435 | |||
1436 | if (ovl->enabled) | ||
1437 | sh_mobile_lcdc_overlay_setup(ovl); | ||
1438 | } | ||
1439 | |||
1440 | return count; | ||
1441 | } | ||
1442 | |||
1443 | static ssize_t | ||
1444 | overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
1445 | { | ||
1446 | struct fb_info *info = dev_get_drvdata(dev); | ||
1447 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1448 | |||
1449 | return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3); | ||
1450 | } | ||
1451 | |||
1452 | static ssize_t | ||
1453 | overlay_rop3_store(struct device *dev, struct device_attribute *attr, | ||
1454 | const char *buf, size_t count) | ||
1455 | { | ||
1456 | struct fb_info *info = dev_get_drvdata(dev); | ||
1457 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1458 | unsigned int rop3; | ||
1459 | char *endp; | ||
1460 | |||
1461 | rop3 = !!simple_strtoul(buf, &endp, 10); | ||
1462 | if (isspace(*endp)) | ||
1463 | endp++; | ||
1464 | |||
1465 | if (endp - buf != count) | ||
1466 | return -EINVAL; | ||
1467 | |||
1468 | if (rop3 > 255) | ||
1469 | return -EINVAL; | ||
1470 | |||
1471 | if (ovl->rop3 != rop3) { | ||
1472 | ovl->rop3 = rop3; | ||
1473 | |||
1474 | if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled) | ||
1475 | sh_mobile_lcdc_overlay_setup(ovl); | ||
1476 | } | ||
1477 | |||
1478 | return count; | ||
1479 | } | ||
1480 | |||
1481 | static const struct device_attribute overlay_sysfs_attrs[] = { | ||
1482 | __ATTR(ovl_alpha, S_IRUGO|S_IWUSR, | ||
1483 | overlay_alpha_show, overlay_alpha_store), | ||
1484 | __ATTR(ovl_mode, S_IRUGO|S_IWUSR, | ||
1485 | overlay_mode_show, overlay_mode_store), | ||
1486 | __ATTR(ovl_position, S_IRUGO|S_IWUSR, | ||
1487 | overlay_position_show, overlay_position_store), | ||
1488 | __ATTR(ovl_rop3, S_IRUGO|S_IWUSR, | ||
1489 | overlay_rop3_show, overlay_rop3_store), | ||
1490 | }; | ||
1491 | |||
1492 | static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = { | ||
1493 | .id = "SH Mobile LCDC", | ||
1494 | .type = FB_TYPE_PACKED_PIXELS, | ||
1495 | .visual = FB_VISUAL_TRUECOLOR, | ||
1496 | .accel = FB_ACCEL_NONE, | ||
1497 | .xpanstep = 1, | ||
1498 | .ypanstep = 1, | ||
1499 | .ywrapstep = 0, | ||
1500 | .capabilities = FB_CAP_FOURCC, | ||
1501 | }; | ||
1502 | |||
1503 | static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var, | ||
1504 | struct fb_info *info) | ||
1505 | { | ||
1506 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1507 | unsigned long base_addr_y; | ||
1508 | unsigned long base_addr_c; | ||
1509 | unsigned long y_offset; | ||
1510 | unsigned long c_offset; | ||
1511 | |||
1512 | if (!ovl->format->yuv) { | ||
1513 | y_offset = (var->yoffset * ovl->xres_virtual + var->xoffset) | ||
1514 | * ovl->format->bpp / 8; | ||
1515 | c_offset = 0; | ||
1516 | } else { | ||
1517 | unsigned int xsub = ovl->format->bpp < 24 ? 2 : 1; | ||
1518 | unsigned int ysub = ovl->format->bpp < 16 ? 2 : 1; | ||
1519 | |||
1520 | y_offset = var->yoffset * ovl->xres_virtual + var->xoffset; | ||
1521 | c_offset = var->yoffset / ysub * ovl->xres_virtual * 2 / xsub | ||
1522 | + var->xoffset * 2 / xsub; | ||
1523 | } | ||
1524 | |||
1525 | /* If the Y offset hasn't changed, the C offset hasn't either. There's | ||
1526 | * nothing to do in that case. | ||
1527 | */ | ||
1528 | if (y_offset == ovl->pan_y_offset) | ||
1529 | return 0; | ||
1530 | |||
1531 | /* Set the source address for the next refresh */ | ||
1532 | base_addr_y = ovl->dma_handle + y_offset; | ||
1533 | base_addr_c = ovl->dma_handle + ovl->xres_virtual * ovl->yres_virtual | ||
1534 | + c_offset; | ||
1535 | |||
1536 | ovl->base_addr_y = base_addr_y; | ||
1537 | ovl->base_addr_c = base_addr_c; | ||
1538 | ovl->pan_y_offset = y_offset; | ||
1539 | |||
1540 | lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); | ||
1541 | |||
1542 | lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); | ||
1543 | lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); | ||
1544 | |||
1545 | lcdc_write(ovl->channel->lcdc, LDBCR, | ||
1546 | LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); | ||
1547 | |||
1548 | return 0; | ||
1549 | } | ||
1550 | |||
1551 | static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd, | ||
1552 | unsigned long arg) | ||
1553 | { | ||
1554 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1555 | |||
1556 | switch (cmd) { | ||
1557 | case FBIO_WAITFORVSYNC: | ||
1558 | return sh_mobile_lcdc_wait_for_vsync(ovl->channel); | ||
1559 | |||
1560 | default: | ||
1561 | return -ENOIOCTLCMD; | ||
1562 | } | ||
1563 | } | ||
1564 | |||
1565 | static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var, | ||
1566 | struct fb_info *info) | ||
1567 | { | ||
1568 | return __sh_mobile_lcdc_check_var(var, info); | ||
1569 | } | ||
1570 | |||
1571 | static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info) | ||
1572 | { | ||
1573 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1574 | |||
1575 | ovl->format = | ||
1576 | sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); | ||
1577 | |||
1578 | ovl->xres = info->var.xres; | ||
1579 | ovl->xres_virtual = info->var.xres_virtual; | ||
1580 | ovl->yres = info->var.yres; | ||
1581 | ovl->yres_virtual = info->var.yres_virtual; | ||
1582 | |||
1583 | if (ovl->format->yuv) | ||
1584 | ovl->pitch = info->var.xres_virtual; | ||
1585 | else | ||
1586 | ovl->pitch = info->var.xres_virtual * ovl->format->bpp / 8; | ||
1587 | |||
1588 | sh_mobile_lcdc_overlay_setup(ovl); | ||
1589 | |||
1590 | info->fix.line_length = ovl->pitch; | ||
1591 | |||
1592 | if (sh_mobile_format_is_fourcc(&info->var)) { | ||
1593 | info->fix.type = FB_TYPE_FOURCC; | ||
1594 | info->fix.visual = FB_VISUAL_FOURCC; | ||
1595 | } else { | ||
1596 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
1597 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
1598 | } | ||
1599 | |||
1600 | return 0; | ||
1601 | } | ||
1602 | |||
1603 | /* Overlay blanking. Disable the overlay when blanked. */ | ||
1604 | static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info) | ||
1605 | { | ||
1606 | struct sh_mobile_lcdc_overlay *ovl = info->par; | ||
1607 | |||
1608 | ovl->enabled = !blank; | ||
1609 | sh_mobile_lcdc_overlay_setup(ovl); | ||
1610 | |||
1611 | /* Prevent the backlight from receiving a blanking event by returning | ||
1612 | * a non-zero value. | ||
1613 | */ | ||
1614 | return 1; | ||
1615 | } | ||
1616 | |||
1617 | static struct fb_ops sh_mobile_lcdc_overlay_ops = { | ||
1618 | .owner = THIS_MODULE, | ||
1619 | .fb_read = fb_sys_read, | ||
1620 | .fb_write = fb_sys_write, | ||
1621 | .fb_fillrect = sys_fillrect, | ||
1622 | .fb_copyarea = sys_copyarea, | ||
1623 | .fb_imageblit = sys_imageblit, | ||
1624 | .fb_blank = sh_mobile_lcdc_overlay_blank, | ||
1625 | .fb_pan_display = sh_mobile_lcdc_overlay_pan, | ||
1626 | .fb_ioctl = sh_mobile_lcdc_overlay_ioctl, | ||
1627 | .fb_check_var = sh_mobile_lcdc_overlay_check_var, | ||
1628 | .fb_set_par = sh_mobile_lcdc_overlay_set_par, | ||
1629 | }; | ||
1630 | |||
1631 | static void | ||
1632 | sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl) | ||
1633 | { | ||
1634 | struct fb_info *info = ovl->info; | ||
1635 | |||
1636 | if (info == NULL || info->dev == NULL) | ||
1637 | return; | ||
1638 | |||
1639 | unregister_framebuffer(ovl->info); | ||
1640 | } | ||
1641 | |||
1642 | static int __devinit | ||
1643 | sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl) | ||
1644 | { | ||
1645 | struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc; | ||
1646 | struct fb_info *info = ovl->info; | ||
1647 | unsigned int i; | ||
1648 | int ret; | ||
1649 | |||
1650 | if (info == NULL) | ||
1651 | return 0; | ||
1652 | |||
1653 | ret = register_framebuffer(info); | ||
1654 | if (ret < 0) | ||
1655 | return ret; | ||
1656 | |||
1657 | dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n", | ||
1658 | dev_name(lcdc->dev), ovl->index, info->var.xres, | ||
1659 | info->var.yres, info->var.bits_per_pixel); | ||
1660 | |||
1661 | for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) { | ||
1662 | ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]); | ||
1663 | if (ret < 0) | ||
1664 | return ret; | ||
1665 | } | ||
1666 | |||
1667 | return 0; | ||
1668 | } | ||
1669 | |||
1670 | static void | ||
1671 | sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl) | ||
1672 | { | ||
1673 | struct fb_info *info = ovl->info; | ||
1674 | |||
1675 | if (info == NULL || info->device == NULL) | ||
1676 | return; | ||
1677 | |||
1678 | framebuffer_release(info); | ||
1679 | } | ||
1680 | |||
1681 | static int __devinit | ||
1682 | sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl) | ||
1683 | { | ||
1684 | struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc; | ||
1685 | struct fb_var_screeninfo *var; | ||
1686 | struct fb_info *info; | ||
1687 | |||
1688 | /* Allocate and initialize the frame buffer device. */ | ||
1689 | info = framebuffer_alloc(0, priv->dev); | ||
1690 | if (info == NULL) { | ||
1691 | dev_err(priv->dev, "unable to allocate fb_info\n"); | ||
1692 | return -ENOMEM; | ||
1693 | } | ||
1694 | |||
1695 | ovl->info = info; | ||
1696 | |||
1697 | info->flags = FBINFO_FLAG_DEFAULT; | ||
1698 | info->fbops = &sh_mobile_lcdc_overlay_ops; | ||
1699 | info->device = priv->dev; | ||
1700 | info->screen_base = ovl->fb_mem; | ||
1701 | info->par = ovl; | ||
1702 | |||
1703 | /* Initialize fixed screen information. Restrict pan to 2 lines steps | ||
1704 | * for NV12 and NV21. | ||
1705 | */ | ||
1706 | info->fix = sh_mobile_lcdc_overlay_fix; | ||
1707 | snprintf(info->fix.id, sizeof(info->fix.id), | ||
1708 | "SH Mobile LCDC Overlay %u", ovl->index); | ||
1709 | info->fix.smem_start = ovl->dma_handle; | ||
1710 | info->fix.smem_len = ovl->fb_size; | ||
1711 | info->fix.line_length = ovl->pitch; | ||
1712 | |||
1713 | if (ovl->format->yuv) | ||
1714 | info->fix.visual = FB_VISUAL_FOURCC; | ||
1715 | else | ||
1716 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
1717 | |||
1718 | switch (ovl->format->fourcc) { | ||
1719 | case V4L2_PIX_FMT_NV12: | ||
1720 | case V4L2_PIX_FMT_NV21: | ||
1721 | info->fix.ypanstep = 2; | ||
1722 | case V4L2_PIX_FMT_NV16: | ||
1723 | case V4L2_PIX_FMT_NV61: | ||
1724 | info->fix.xpanstep = 2; | ||
1725 | } | ||
1726 | |||
1727 | /* Initialize variable screen information. */ | ||
1728 | var = &info->var; | ||
1729 | memset(var, 0, sizeof(*var)); | ||
1730 | var->xres = ovl->xres; | ||
1731 | var->yres = ovl->yres; | ||
1732 | var->xres_virtual = ovl->xres_virtual; | ||
1733 | var->yres_virtual = ovl->yres_virtual; | ||
1734 | var->activate = FB_ACTIVATE_NOW; | ||
1735 | |||
1736 | /* Use the legacy API by default for RGB formats, and the FOURCC API | ||
1737 | * for YUV formats. | ||
1738 | */ | ||
1739 | if (!ovl->format->yuv) | ||
1740 | var->bits_per_pixel = ovl->format->bpp; | ||
1741 | else | ||
1742 | var->grayscale = ovl->format->fourcc; | ||
1743 | |||
1744 | return sh_mobile_lcdc_overlay_check_var(var, info); | ||
1745 | } | ||
1746 | |||
1747 | /* ----------------------------------------------------------------------------- | ||
1748 | * Frame buffer operations - main frame buffer | ||
980 | */ | 1749 | */ |
981 | 1750 | ||
982 | static int sh_mobile_lcdc_setcolreg(u_int regno, | 1751 | static int sh_mobile_lcdc_setcolreg(u_int regno, |
@@ -1003,12 +1772,12 @@ static int sh_mobile_lcdc_setcolreg(u_int regno, | |||
1003 | return 0; | 1772 | return 0; |
1004 | } | 1773 | } |
1005 | 1774 | ||
1006 | static struct fb_fix_screeninfo sh_mobile_lcdc_fix = { | 1775 | static const struct fb_fix_screeninfo sh_mobile_lcdc_fix = { |
1007 | .id = "SH Mobile LCDC", | 1776 | .id = "SH Mobile LCDC", |
1008 | .type = FB_TYPE_PACKED_PIXELS, | 1777 | .type = FB_TYPE_PACKED_PIXELS, |
1009 | .visual = FB_VISUAL_TRUECOLOR, | 1778 | .visual = FB_VISUAL_TRUECOLOR, |
1010 | .accel = FB_ACCEL_NONE, | 1779 | .accel = FB_ACCEL_NONE, |
1011 | .xpanstep = 0, | 1780 | .xpanstep = 1, |
1012 | .ypanstep = 1, | 1781 | .ypanstep = 1, |
1013 | .ywrapstep = 0, | 1782 | .ywrapstep = 0, |
1014 | .capabilities = FB_CAP_FOURCC, | 1783 | .capabilities = FB_CAP_FOURCC, |
@@ -1035,78 +1804,74 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info, | |||
1035 | sh_mobile_lcdc_deferred_io_touch(info); | 1804 | sh_mobile_lcdc_deferred_io_touch(info); |
1036 | } | 1805 | } |
1037 | 1806 | ||
1038 | static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, | 1807 | static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var, |
1039 | struct fb_info *info) | 1808 | struct fb_info *info) |
1040 | { | 1809 | { |
1041 | struct sh_mobile_lcdc_chan *ch = info->par; | 1810 | struct sh_mobile_lcdc_chan *ch = info->par; |
1042 | struct sh_mobile_lcdc_priv *priv = ch->lcdc; | 1811 | struct sh_mobile_lcdc_priv *priv = ch->lcdc; |
1043 | unsigned long ldrcntr; | 1812 | unsigned long ldrcntr; |
1044 | unsigned long new_pan_offset; | ||
1045 | unsigned long base_addr_y, base_addr_c; | 1813 | unsigned long base_addr_y, base_addr_c; |
1814 | unsigned long y_offset; | ||
1046 | unsigned long c_offset; | 1815 | unsigned long c_offset; |
1047 | 1816 | ||
1048 | if (!ch->format->yuv) | 1817 | if (!ch->format->yuv) { |
1049 | new_pan_offset = var->yoffset * ch->pitch | 1818 | y_offset = (var->yoffset * ch->xres_virtual + var->xoffset) |
1050 | + var->xoffset * (ch->format->bpp / 8); | 1819 | * ch->format->bpp / 8; |
1051 | else | 1820 | c_offset = 0; |
1052 | new_pan_offset = var->yoffset * ch->pitch + var->xoffset; | 1821 | } else { |
1822 | unsigned int xsub = ch->format->bpp < 24 ? 2 : 1; | ||
1823 | unsigned int ysub = ch->format->bpp < 16 ? 2 : 1; | ||
1053 | 1824 | ||
1054 | if (new_pan_offset == ch->pan_offset) | 1825 | y_offset = var->yoffset * ch->xres_virtual + var->xoffset; |
1055 | return 0; /* No change, do nothing */ | 1826 | c_offset = var->yoffset / ysub * ch->xres_virtual * 2 / xsub |
1827 | + var->xoffset * 2 / xsub; | ||
1828 | } | ||
1056 | 1829 | ||
1057 | ldrcntr = lcdc_read(priv, _LDRCNTR); | 1830 | /* If the Y offset hasn't changed, the C offset hasn't either. There's |
1831 | * nothing to do in that case. | ||
1832 | */ | ||
1833 | if (y_offset == ch->pan_y_offset) | ||
1834 | return 0; | ||
1058 | 1835 | ||
1059 | /* Set the source address for the next refresh */ | 1836 | /* Set the source address for the next refresh */ |
1060 | base_addr_y = ch->dma_handle + new_pan_offset; | 1837 | base_addr_y = ch->dma_handle + y_offset; |
1061 | if (ch->format->yuv) { | 1838 | base_addr_c = ch->dma_handle + ch->xres_virtual * ch->yres_virtual |
1062 | /* Set y offset */ | 1839 | + c_offset; |
1063 | c_offset = var->yoffset * ch->pitch | ||
1064 | * (ch->format->bpp - 8) / 8; | ||
1065 | base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual | ||
1066 | + c_offset; | ||
1067 | /* Set x offset */ | ||
1068 | if (ch->format->fourcc == V4L2_PIX_FMT_NV24) | ||
1069 | base_addr_c += 2 * var->xoffset; | ||
1070 | else | ||
1071 | base_addr_c += var->xoffset; | ||
1072 | } | ||
1073 | 1840 | ||
1074 | if (ch->meram) { | 1841 | if (ch->cache) |
1075 | struct sh_mobile_meram_info *mdev; | 1842 | sh_mobile_meram_cache_update(priv->meram_dev, ch->cache, |
1076 | 1843 | base_addr_y, base_addr_c, | |
1077 | mdev = priv->meram_dev; | 1844 | &base_addr_y, &base_addr_c); |
1078 | mdev->ops->meram_update(mdev, ch->meram, | ||
1079 | base_addr_y, base_addr_c, | ||
1080 | &base_addr_y, &base_addr_c); | ||
1081 | } | ||
1082 | 1845 | ||
1083 | ch->base_addr_y = base_addr_y; | 1846 | ch->base_addr_y = base_addr_y; |
1084 | ch->base_addr_c = base_addr_c; | 1847 | ch->base_addr_c = base_addr_c; |
1848 | ch->pan_y_offset = y_offset; | ||
1085 | 1849 | ||
1086 | lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); | 1850 | lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); |
1087 | if (ch->format->yuv) | 1851 | if (ch->format->yuv) |
1088 | lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); | 1852 | lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); |
1089 | 1853 | ||
1854 | ldrcntr = lcdc_read(priv, _LDRCNTR); | ||
1090 | if (lcdc_chan_is_sublcd(ch)) | 1855 | if (lcdc_chan_is_sublcd(ch)) |
1091 | lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); | 1856 | lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); |
1092 | else | 1857 | else |
1093 | lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS); | 1858 | lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS); |
1094 | 1859 | ||
1095 | ch->pan_offset = new_pan_offset; | ||
1096 | 1860 | ||
1097 | sh_mobile_lcdc_deferred_io_touch(info); | 1861 | sh_mobile_lcdc_deferred_io_touch(info); |
1098 | 1862 | ||
1099 | return 0; | 1863 | return 0; |
1100 | } | 1864 | } |
1101 | 1865 | ||
1102 | static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, | 1866 | static int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd, |
1103 | unsigned long arg) | 1867 | unsigned long arg) |
1104 | { | 1868 | { |
1869 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
1105 | int retval; | 1870 | int retval; |
1106 | 1871 | ||
1107 | switch (cmd) { | 1872 | switch (cmd) { |
1108 | case FBIO_WAITFORVSYNC: | 1873 | case FBIO_WAITFORVSYNC: |
1109 | retval = sh_mobile_wait_for_vsync(info->par); | 1874 | retval = sh_mobile_lcdc_wait_for_vsync(ch); |
1110 | break; | 1875 | break; |
1111 | 1876 | ||
1112 | default: | 1877 | default: |
@@ -1158,7 +1923,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 | 1923 | * 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. | 1924 | * user == 1, or with console sem held, if user == 0. |
1160 | */ | 1925 | */ |
1161 | static int sh_mobile_release(struct fb_info *info, int user) | 1926 | static int sh_mobile_lcdc_release(struct fb_info *info, int user) |
1162 | { | 1927 | { |
1163 | struct sh_mobile_lcdc_chan *ch = info->par; | 1928 | struct sh_mobile_lcdc_chan *ch = info->par; |
1164 | 1929 | ||
@@ -1179,7 +1944,7 @@ static int sh_mobile_release(struct fb_info *info, int user) | |||
1179 | return 0; | 1944 | return 0; |
1180 | } | 1945 | } |
1181 | 1946 | ||
1182 | static int sh_mobile_open(struct fb_info *info, int user) | 1947 | static int sh_mobile_lcdc_open(struct fb_info *info, int user) |
1183 | { | 1948 | { |
1184 | struct sh_mobile_lcdc_chan *ch = info->par; | 1949 | struct sh_mobile_lcdc_chan *ch = info->par; |
1185 | 1950 | ||
@@ -1192,7 +1957,8 @@ static int sh_mobile_open(struct fb_info *info, int user) | |||
1192 | return 0; | 1957 | return 0; |
1193 | } | 1958 | } |
1194 | 1959 | ||
1195 | static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 1960 | static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, |
1961 | struct fb_info *info) | ||
1196 | { | 1962 | { |
1197 | struct sh_mobile_lcdc_chan *ch = info->par; | 1963 | struct sh_mobile_lcdc_chan *ch = info->par; |
1198 | struct sh_mobile_lcdc_priv *p = ch->lcdc; | 1964 | struct sh_mobile_lcdc_priv *p = ch->lcdc; |
@@ -1200,9 +1966,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
1200 | unsigned int best_xres = 0; | 1966 | unsigned int best_xres = 0; |
1201 | unsigned int best_yres = 0; | 1967 | unsigned int best_yres = 0; |
1202 | unsigned int i; | 1968 | unsigned int i; |
1203 | 1969 | int ret; | |
1204 | if (var->xres > MAX_XRES || var->yres > MAX_YRES) | ||
1205 | return -EINVAL; | ||
1206 | 1970 | ||
1207 | /* If board code provides us with a list of available modes, make sure | 1971 | /* 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 | 1972 | * we use one of them. Find the mode closest to the requested one. The |
@@ -1237,73 +2001,9 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
1237 | var->yres = best_yres; | 2001 | var->yres = best_yres; |
1238 | } | 2002 | } |
1239 | 2003 | ||
1240 | /* Make sure the virtual resolution is at least as big as the visible | 2004 | ret = __sh_mobile_lcdc_check_var(var, info); |
1241 | * resolution. | 2005 | if (ret < 0) |
1242 | */ | 2006 | 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 | 2007 | ||
1308 | /* only accept the forced_fourcc for dual channel configurations */ | 2008 | /* only accept the forced_fourcc for dual channel configurations */ |
1309 | if (p->forced_fourcc && | 2009 | if (p->forced_fourcc && |
@@ -1313,7 +2013,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
1313 | return 0; | 2013 | return 0; |
1314 | } | 2014 | } |
1315 | 2015 | ||
1316 | static int sh_mobile_set_par(struct fb_info *info) | 2016 | static int sh_mobile_lcdc_set_par(struct fb_info *info) |
1317 | { | 2017 | { |
1318 | struct sh_mobile_lcdc_chan *ch = info->par; | 2018 | struct sh_mobile_lcdc_chan *ch = info->par; |
1319 | int ret; | 2019 | int ret; |
@@ -1329,9 +2029,9 @@ static int sh_mobile_set_par(struct fb_info *info) | |||
1329 | ch->yres_virtual = info->var.yres_virtual; | 2029 | ch->yres_virtual = info->var.yres_virtual; |
1330 | 2030 | ||
1331 | if (ch->format->yuv) | 2031 | if (ch->format->yuv) |
1332 | ch->pitch = info->var.xres; | 2032 | ch->pitch = info->var.xres_virtual; |
1333 | else | 2033 | else |
1334 | ch->pitch = info->var.xres * ch->format->bpp / 8; | 2034 | ch->pitch = info->var.xres_virtual * ch->format->bpp / 8; |
1335 | 2035 | ||
1336 | ret = sh_mobile_lcdc_start(ch->lcdc); | 2036 | ret = sh_mobile_lcdc_start(ch->lcdc); |
1337 | if (ret < 0) | 2037 | if (ret < 0) |
@@ -1383,8 +2083,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, | 2083 | * mode will reenable the clocks and update the screen in time, |
1384 | * so it does not need this. */ | 2084 | * so it does not need this. */ |
1385 | if (!info->fbdefio) { | 2085 | if (!info->fbdefio) { |
1386 | sh_mobile_wait_for_vsync(ch); | 2086 | sh_mobile_lcdc_wait_for_vsync(ch); |
1387 | sh_mobile_wait_for_vsync(ch); | 2087 | sh_mobile_lcdc_wait_for_vsync(ch); |
1388 | } | 2088 | } |
1389 | sh_mobile_lcdc_clk_off(p); | 2089 | sh_mobile_lcdc_clk_off(p); |
1390 | } | 2090 | } |
@@ -1402,12 +2102,12 @@ static struct fb_ops sh_mobile_lcdc_ops = { | |||
1402 | .fb_copyarea = sh_mobile_lcdc_copyarea, | 2102 | .fb_copyarea = sh_mobile_lcdc_copyarea, |
1403 | .fb_imageblit = sh_mobile_lcdc_imageblit, | 2103 | .fb_imageblit = sh_mobile_lcdc_imageblit, |
1404 | .fb_blank = sh_mobile_lcdc_blank, | 2104 | .fb_blank = sh_mobile_lcdc_blank, |
1405 | .fb_pan_display = sh_mobile_fb_pan_display, | 2105 | .fb_pan_display = sh_mobile_lcdc_pan, |
1406 | .fb_ioctl = sh_mobile_ioctl, | 2106 | .fb_ioctl = sh_mobile_lcdc_ioctl, |
1407 | .fb_open = sh_mobile_open, | 2107 | .fb_open = sh_mobile_lcdc_open, |
1408 | .fb_release = sh_mobile_release, | 2108 | .fb_release = sh_mobile_lcdc_release, |
1409 | .fb_check_var = sh_mobile_check_var, | 2109 | .fb_check_var = sh_mobile_lcdc_check_var, |
1410 | .fb_set_par = sh_mobile_set_par, | 2110 | .fb_set_par = sh_mobile_lcdc_set_par, |
1411 | }; | 2111 | }; |
1412 | 2112 | ||
1413 | static void | 2113 | static void |
@@ -1514,19 +2214,24 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, | |||
1514 | else | 2214 | else |
1515 | info->fix.visual = FB_VISUAL_TRUECOLOR; | 2215 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
1516 | 2216 | ||
1517 | if (ch->format->fourcc == V4L2_PIX_FMT_NV12 || | 2217 | switch (ch->format->fourcc) { |
1518 | ch->format->fourcc == V4L2_PIX_FMT_NV21) | 2218 | case V4L2_PIX_FMT_NV12: |
2219 | case V4L2_PIX_FMT_NV21: | ||
1519 | info->fix.ypanstep = 2; | 2220 | info->fix.ypanstep = 2; |
2221 | case V4L2_PIX_FMT_NV16: | ||
2222 | case V4L2_PIX_FMT_NV61: | ||
2223 | info->fix.xpanstep = 2; | ||
2224 | } | ||
1520 | 2225 | ||
1521 | /* Initialize variable screen information using the first mode as | 2226 | /* Initialize variable screen information using the first mode as |
1522 | * default. The default Y virtual resolution is twice the panel size to | 2227 | * default. |
1523 | * allow for double-buffering. | ||
1524 | */ | 2228 | */ |
1525 | var = &info->var; | 2229 | var = &info->var; |
1526 | fb_videomode_to_var(var, mode); | 2230 | fb_videomode_to_var(var, mode); |
1527 | var->width = ch->cfg->panel_cfg.width; | 2231 | var->width = ch->cfg->panel_cfg.width; |
1528 | var->height = ch->cfg->panel_cfg.height; | 2232 | var->height = ch->cfg->panel_cfg.height; |
1529 | var->yres_virtual = var->yres * 2; | 2233 | var->xres_virtual = ch->xres_virtual; |
2234 | var->yres_virtual = ch->yres_virtual; | ||
1530 | var->activate = FB_ACTIVATE_NOW; | 2235 | var->activate = FB_ACTIVATE_NOW; |
1531 | 2236 | ||
1532 | /* Use the legacy API by default for RGB formats, and the FOURCC API | 2237 | /* Use the legacy API by default for RGB formats, and the FOURCC API |
@@ -1537,7 +2242,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, | |||
1537 | else | 2242 | else |
1538 | var->grayscale = ch->format->fourcc; | 2243 | var->grayscale = ch->format->fourcc; |
1539 | 2244 | ||
1540 | ret = sh_mobile_check_var(var, info); | 2245 | ret = sh_mobile_lcdc_check_var(var, info); |
1541 | if (ret) | 2246 | if (ret) |
1542 | return ret; | 2247 | return ret; |
1543 | 2248 | ||
@@ -1712,15 +2417,27 @@ static const struct fb_videomode default_720p __devinitconst = { | |||
1712 | static int sh_mobile_lcdc_remove(struct platform_device *pdev) | 2417 | static int sh_mobile_lcdc_remove(struct platform_device *pdev) |
1713 | { | 2418 | { |
1714 | struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); | 2419 | struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); |
1715 | int i; | 2420 | unsigned int i; |
1716 | 2421 | ||
1717 | fb_unregister_client(&priv->notifier); | 2422 | fb_unregister_client(&priv->notifier); |
1718 | 2423 | ||
2424 | for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) | ||
2425 | sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]); | ||
1719 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) | 2426 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) |
1720 | sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); | 2427 | sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); |
1721 | 2428 | ||
1722 | sh_mobile_lcdc_stop(priv); | 2429 | sh_mobile_lcdc_stop(priv); |
1723 | 2430 | ||
2431 | for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) { | ||
2432 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; | ||
2433 | |||
2434 | sh_mobile_lcdc_overlay_fb_cleanup(ovl); | ||
2435 | |||
2436 | if (ovl->fb_mem) | ||
2437 | dma_free_coherent(&pdev->dev, ovl->fb_size, | ||
2438 | ovl->fb_mem, ovl->dma_handle); | ||
2439 | } | ||
2440 | |||
1724 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { | 2441 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { |
1725 | struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; | 2442 | struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; |
1726 | 2443 | ||
@@ -1737,8 +2454,11 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) | |||
1737 | } | 2454 | } |
1738 | 2455 | ||
1739 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { | 2456 | for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { |
1740 | if (priv->ch[i].bl) | 2457 | struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; |
1741 | sh_mobile_lcdc_bl_remove(priv->ch[i].bl); | 2458 | |
2459 | if (ch->bl) | ||
2460 | sh_mobile_lcdc_bl_remove(ch->bl); | ||
2461 | mutex_destroy(&ch->open_lock); | ||
1742 | } | 2462 | } |
1743 | 2463 | ||
1744 | if (priv->dot_clk) { | 2464 | if (priv->dot_clk) { |
@@ -1796,6 +2516,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan * | |||
1796 | } | 2516 | } |
1797 | 2517 | ||
1798 | static int __devinit | 2518 | static int __devinit |
2519 | sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv, | ||
2520 | struct sh_mobile_lcdc_overlay *ovl) | ||
2521 | { | ||
2522 | const struct sh_mobile_lcdc_format_info *format; | ||
2523 | int ret; | ||
2524 | |||
2525 | if (ovl->cfg->fourcc == 0) | ||
2526 | return 0; | ||
2527 | |||
2528 | /* Validate the format. */ | ||
2529 | format = sh_mobile_format_info(ovl->cfg->fourcc); | ||
2530 | if (format == NULL) { | ||
2531 | dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc); | ||
2532 | return -EINVAL; | ||
2533 | } | ||
2534 | |||
2535 | ovl->enabled = false; | ||
2536 | ovl->mode = LCDC_OVERLAY_BLEND; | ||
2537 | ovl->alpha = 255; | ||
2538 | ovl->rop3 = 0; | ||
2539 | ovl->pos_x = 0; | ||
2540 | ovl->pos_y = 0; | ||
2541 | |||
2542 | /* The default Y virtual resolution is twice the panel size to allow for | ||
2543 | * double-buffering. | ||
2544 | */ | ||
2545 | ovl->format = format; | ||
2546 | ovl->xres = ovl->cfg->max_xres; | ||
2547 | ovl->xres_virtual = ovl->xres; | ||
2548 | ovl->yres = ovl->cfg->max_yres; | ||
2549 | ovl->yres_virtual = ovl->yres * 2; | ||
2550 | |||
2551 | if (!format->yuv) | ||
2552 | ovl->pitch = ovl->xres_virtual * format->bpp / 8; | ||
2553 | else | ||
2554 | ovl->pitch = ovl->xres_virtual; | ||
2555 | |||
2556 | /* Allocate frame buffer memory. */ | ||
2557 | ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres | ||
2558 | * format->bpp / 8 * 2; | ||
2559 | ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size, | ||
2560 | &ovl->dma_handle, GFP_KERNEL); | ||
2561 | if (!ovl->fb_mem) { | ||
2562 | dev_err(priv->dev, "unable to allocate buffer\n"); | ||
2563 | return -ENOMEM; | ||
2564 | } | ||
2565 | |||
2566 | ret = sh_mobile_lcdc_overlay_fb_init(ovl); | ||
2567 | if (ret < 0) | ||
2568 | return ret; | ||
2569 | |||
2570 | return 0; | ||
2571 | } | ||
2572 | |||
2573 | static int __devinit | ||
1799 | sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, | 2574 | sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, |
1800 | struct sh_mobile_lcdc_chan *ch) | 2575 | struct sh_mobile_lcdc_chan *ch) |
1801 | { | 2576 | { |
@@ -1854,7 +2629,9 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, | |||
1854 | num_modes = cfg->num_modes; | 2629 | num_modes = cfg->num_modes; |
1855 | } | 2630 | } |
1856 | 2631 | ||
1857 | /* Use the first mode as default. */ | 2632 | /* Use the first mode as default. The default Y virtual resolution is |
2633 | * twice the panel size to allow for double-buffering. | ||
2634 | */ | ||
1858 | ch->format = format; | 2635 | ch->format = format; |
1859 | ch->xres = mode->xres; | 2636 | ch->xres = mode->xres; |
1860 | ch->xres_virtual = mode->xres; | 2637 | ch->xres_virtual = mode->xres; |
@@ -1863,10 +2640,10 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv, | |||
1863 | 2640 | ||
1864 | if (!format->yuv) { | 2641 | if (!format->yuv) { |
1865 | ch->colorspace = V4L2_COLORSPACE_SRGB; | 2642 | ch->colorspace = V4L2_COLORSPACE_SRGB; |
1866 | ch->pitch = ch->xres * format->bpp / 8; | 2643 | ch->pitch = ch->xres_virtual * format->bpp / 8; |
1867 | } else { | 2644 | } else { |
1868 | ch->colorspace = V4L2_COLORSPACE_REC709; | 2645 | ch->colorspace = V4L2_COLORSPACE_REC709; |
1869 | ch->pitch = ch->xres; | 2646 | ch->pitch = ch->xres_virtual; |
1870 | } | 2647 | } |
1871 | 2648 | ||
1872 | ch->display.width = cfg->panel_cfg.width; | 2649 | ch->display.width = cfg->panel_cfg.width; |
@@ -1952,7 +2729,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1952 | } | 2729 | } |
1953 | init_waitqueue_head(&ch->frame_end_wait); | 2730 | init_waitqueue_head(&ch->frame_end_wait); |
1954 | init_completion(&ch->vsync_completion); | 2731 | init_completion(&ch->vsync_completion); |
1955 | ch->pan_offset = 0; | ||
1956 | 2732 | ||
1957 | /* probe the backlight is there is one defined */ | 2733 | /* probe the backlight is there is one defined */ |
1958 | if (ch->cfg->bl_info.max_brightness) | 2734 | if (ch->cfg->bl_info.max_brightness) |
@@ -2003,6 +2779,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
2003 | goto err1; | 2779 | goto err1; |
2004 | } | 2780 | } |
2005 | 2781 | ||
2782 | for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { | ||
2783 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; | ||
2784 | |||
2785 | ovl->cfg = &pdata->overlays[i]; | ||
2786 | ovl->channel = &priv->ch[0]; | ||
2787 | |||
2788 | error = sh_mobile_lcdc_overlay_init(priv, ovl); | ||
2789 | if (error) | ||
2790 | goto err1; | ||
2791 | } | ||
2792 | |||
2006 | error = sh_mobile_lcdc_start(priv); | 2793 | error = sh_mobile_lcdc_start(priv); |
2007 | if (error) { | 2794 | if (error) { |
2008 | dev_err(&pdev->dev, "unable to start hardware\n"); | 2795 | dev_err(&pdev->dev, "unable to start hardware\n"); |
@@ -2017,6 +2804,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
2017 | goto err1; | 2804 | goto err1; |
2018 | } | 2805 | } |
2019 | 2806 | ||
2807 | for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { | ||
2808 | struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; | ||
2809 | |||
2810 | error = sh_mobile_lcdc_overlay_fb_register(ovl); | ||
2811 | if (error) | ||
2812 | goto err1; | ||
2813 | } | ||
2814 | |||
2020 | /* Failure ignored */ | 2815 | /* Failure ignored */ |
2021 | priv->notifier.notifier_call = sh_mobile_lcdc_notify; | 2816 | priv->notifier.notifier_call = sh_mobile_lcdc_notify; |
2022 | fb_register_client(&priv->notifier); | 2817 | fb_register_client(&priv->notifier); |