diff options
author | Pawel Osciak <p.osciak@samsung.com> | 2010-08-10 21:02:38 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-11 11:59:10 -0400 |
commit | efdc846d2f7190c8f3092c09975c0ebba30a95ba (patch) | |
tree | 23834f2ab4523b86c53f2a4f3b56834e8155dfb2 | |
parent | 067b226b9e8e20463e0937344c93101ac8d8d2b1 (diff) |
s3c-fb: add wait for VSYNC ioctl
Add VSYNC interrupt support and an ioctl that allows waiting for it.
Interrupts are turned on only when needed.
Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/arm/plat-samsung/include/plat/regs-fb.h | 1 | ||||
-rw-r--r-- | drivers/video/s3c-fb.c | 166 |
2 files changed, 166 insertions, 1 deletions
diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h b/arch/arm/plat-samsung/include/plat/regs-fb.h index dbb6c0afef47..564594e89efe 100644 --- a/arch/arm/plat-samsung/include/plat/regs-fb.h +++ b/arch/arm/plat-samsung/include/plat/regs-fb.h | |||
@@ -300,6 +300,7 @@ | |||
300 | #define VIDINTCON0_FRAMESEL0_FRONTPORCH (0x3 << 15) | 300 | #define VIDINTCON0_FRAMESEL0_FRONTPORCH (0x3 << 15) |
301 | 301 | ||
302 | #define VIDINTCON0_FRAMESEL1 (1 << 13) | 302 | #define VIDINTCON0_FRAMESEL1 (1 << 13) |
303 | #define VIDINTCON0_FRAMESEL1_MASK (0x3 << 13) | ||
303 | #define VIDINTCON0_FRAMESEL1_NONE (0x0 << 13) | 304 | #define VIDINTCON0_FRAMESEL1_NONE (0x0 << 13) |
304 | #define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 13) | 305 | #define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 13) |
305 | #define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 13) | 306 | #define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 13) |
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index de427d928f48..2e82adf9ab6d 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c | |||
@@ -21,6 +21,8 @@ | |||
21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
22 | #include <linux/fb.h> | 22 | #include <linux/fb.h> |
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/uaccess.h> | ||
25 | #include <linux/interrupt.h> | ||
24 | 26 | ||
25 | #include <mach/map.h> | 27 | #include <mach/map.h> |
26 | #include <plat/regs-fb-v4.h> | 28 | #include <plat/regs-fb-v4.h> |
@@ -48,6 +50,11 @@ | |||
48 | __raw_writel(v, r); } while(0) | 50 | __raw_writel(v, r); } while(0) |
49 | #endif /* FB_S3C_DEBUG_REGWRITE */ | 51 | #endif /* FB_S3C_DEBUG_REGWRITE */ |
50 | 52 | ||
53 | /* irq_flags bits */ | ||
54 | #define S3C_FB_VSYNC_IRQ_EN 0 | ||
55 | |||
56 | #define VSYNC_TIMEOUT_MSEC 50 | ||
57 | |||
51 | struct s3c_fb; | 58 | struct s3c_fb; |
52 | 59 | ||
53 | #define VALID_BPP(x) (1 << ((x) - 1)) | 60 | #define VALID_BPP(x) (1 << ((x) - 1)) |
@@ -156,6 +163,16 @@ struct s3c_fb_win { | |||
156 | }; | 163 | }; |
157 | 164 | ||
158 | /** | 165 | /** |
166 | * struct s3c_fb_vsync - vsync information | ||
167 | * @wait: a queue for processes waiting for vsync | ||
168 | * @count: vsync interrupt count | ||
169 | */ | ||
170 | struct s3c_fb_vsync { | ||
171 | wait_queue_head_t wait; | ||
172 | unsigned int count; | ||
173 | }; | ||
174 | |||
175 | /** | ||
159 | * struct s3c_fb - overall hardware state of the hardware | 176 | * struct s3c_fb - overall hardware state of the hardware |
160 | * @dev: The device that we bound to, for printing, etc. | 177 | * @dev: The device that we bound to, for printing, etc. |
161 | * @regs_res: The resource we claimed for the IO registers. | 178 | * @regs_res: The resource we claimed for the IO registers. |
@@ -165,6 +182,9 @@ struct s3c_fb_win { | |||
165 | * @enabled: A bitmask of enabled hardware windows. | 182 | * @enabled: A bitmask of enabled hardware windows. |
166 | * @pdata: The platform configuration data passed with the device. | 183 | * @pdata: The platform configuration data passed with the device. |
167 | * @windows: The hardware windows that have been claimed. | 184 | * @windows: The hardware windows that have been claimed. |
185 | * @irq_no: IRQ line number | ||
186 | * @irq_flags: irq flags | ||
187 | * @vsync_info: VSYNC-related information (count, queues...) | ||
168 | */ | 188 | */ |
169 | struct s3c_fb { | 189 | struct s3c_fb { |
170 | struct device *dev; | 190 | struct device *dev; |
@@ -177,6 +197,10 @@ struct s3c_fb { | |||
177 | 197 | ||
178 | struct s3c_fb_platdata *pdata; | 198 | struct s3c_fb_platdata *pdata; |
179 | struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; | 199 | struct s3c_fb_win *windows[S3C_FB_MAX_WIN]; |
200 | |||
201 | int irq_no; | ||
202 | unsigned long irq_flags; | ||
203 | struct s3c_fb_vsync vsync_info; | ||
180 | }; | 204 | }; |
181 | 205 | ||
182 | /** | 206 | /** |
@@ -798,6 +822,124 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var, | |||
798 | return 0; | 822 | return 0; |
799 | } | 823 | } |
800 | 824 | ||
825 | /** | ||
826 | * s3c_fb_enable_irq() - enable framebuffer interrupts | ||
827 | * @sfb: main hardware state | ||
828 | */ | ||
829 | static void s3c_fb_enable_irq(struct s3c_fb *sfb) | ||
830 | { | ||
831 | void __iomem *regs = sfb->regs; | ||
832 | u32 irq_ctrl_reg; | ||
833 | |||
834 | if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { | ||
835 | /* IRQ disabled, enable it */ | ||
836 | irq_ctrl_reg = readl(regs + VIDINTCON0); | ||
837 | |||
838 | irq_ctrl_reg |= VIDINTCON0_INT_ENABLE; | ||
839 | irq_ctrl_reg |= VIDINTCON0_INT_FRAME; | ||
840 | |||
841 | irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK; | ||
842 | irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC; | ||
843 | irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK; | ||
844 | irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE; | ||
845 | |||
846 | writel(irq_ctrl_reg, regs + VIDINTCON0); | ||
847 | } | ||
848 | } | ||
849 | |||
850 | /** | ||
851 | * s3c_fb_disable_irq() - disable framebuffer interrupts | ||
852 | * @sfb: main hardware state | ||
853 | */ | ||
854 | static void s3c_fb_disable_irq(struct s3c_fb *sfb) | ||
855 | { | ||
856 | void __iomem *regs = sfb->regs; | ||
857 | u32 irq_ctrl_reg; | ||
858 | |||
859 | if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) { | ||
860 | /* IRQ enabled, disable it */ | ||
861 | irq_ctrl_reg = readl(regs + VIDINTCON0); | ||
862 | |||
863 | irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME; | ||
864 | irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE; | ||
865 | |||
866 | writel(irq_ctrl_reg, regs + VIDINTCON0); | ||
867 | } | ||
868 | } | ||
869 | |||
870 | static irqreturn_t s3c_fb_irq(int irq, void *dev_id) | ||
871 | { | ||
872 | struct s3c_fb *sfb = dev_id; | ||
873 | void __iomem *regs = sfb->regs; | ||
874 | u32 irq_sts_reg; | ||
875 | |||
876 | irq_sts_reg = readl(regs + VIDINTCON1); | ||
877 | |||
878 | if (irq_sts_reg & VIDINTCON1_INT_FRAME) { | ||
879 | |||
880 | /* VSYNC interrupt, accept it */ | ||
881 | writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1); | ||
882 | |||
883 | sfb->vsync_info.count++; | ||
884 | wake_up_interruptible(&sfb->vsync_info.wait); | ||
885 | } | ||
886 | |||
887 | /* We only support waiting for VSYNC for now, so it's safe | ||
888 | * to always disable irqs here. | ||
889 | */ | ||
890 | s3c_fb_disable_irq(sfb); | ||
891 | |||
892 | return IRQ_HANDLED; | ||
893 | } | ||
894 | |||
895 | /** | ||
896 | * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout | ||
897 | * @sfb: main hardware state | ||
898 | * @crtc: head index. | ||
899 | */ | ||
900 | static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc) | ||
901 | { | ||
902 | unsigned long count; | ||
903 | int ret; | ||
904 | |||
905 | if (crtc != 0) | ||
906 | return -ENODEV; | ||
907 | |||
908 | count = sfb->vsync_info.count; | ||
909 | s3c_fb_enable_irq(sfb); | ||
910 | ret = wait_event_interruptible_timeout(sfb->vsync_info.wait, | ||
911 | count != sfb->vsync_info.count, | ||
912 | msecs_to_jiffies(VSYNC_TIMEOUT_MSEC)); | ||
913 | if (ret == 0) | ||
914 | return -ETIMEDOUT; | ||
915 | |||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, | ||
920 | unsigned long arg) | ||
921 | { | ||
922 | struct s3c_fb_win *win = info->par; | ||
923 | struct s3c_fb *sfb = win->parent; | ||
924 | int ret; | ||
925 | u32 crtc; | ||
926 | |||
927 | switch (cmd) { | ||
928 | case FBIO_WAITFORVSYNC: | ||
929 | if (get_user(crtc, (u32 __user *)arg)) { | ||
930 | ret = -EFAULT; | ||
931 | break; | ||
932 | } | ||
933 | |||
934 | ret = s3c_fb_wait_for_vsync(sfb, crtc); | ||
935 | break; | ||
936 | default: | ||
937 | ret = -ENOTTY; | ||
938 | } | ||
939 | |||
940 | return ret; | ||
941 | } | ||
942 | |||
801 | static struct fb_ops s3c_fb_ops = { | 943 | static struct fb_ops s3c_fb_ops = { |
802 | .owner = THIS_MODULE, | 944 | .owner = THIS_MODULE, |
803 | .fb_check_var = s3c_fb_check_var, | 945 | .fb_check_var = s3c_fb_check_var, |
@@ -808,6 +950,7 @@ static struct fb_ops s3c_fb_ops = { | |||
808 | .fb_copyarea = cfb_copyarea, | 950 | .fb_copyarea = cfb_copyarea, |
809 | .fb_imageblit = cfb_imageblit, | 951 | .fb_imageblit = cfb_imageblit, |
810 | .fb_pan_display = s3c_fb_pan_display, | 952 | .fb_pan_display = s3c_fb_pan_display, |
953 | .fb_ioctl = s3c_fb_ioctl, | ||
811 | }; | 954 | }; |
812 | 955 | ||
813 | /** | 956 | /** |
@@ -914,6 +1057,8 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, | |||
914 | 1057 | ||
915 | dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant); | 1058 | dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant); |
916 | 1059 | ||
1060 | init_waitqueue_head(&sfb->vsync_info.wait); | ||
1061 | |||
917 | palette_size = variant->palette_sz * 4; | 1062 | palette_size = variant->palette_sz * 4; |
918 | 1063 | ||
919 | fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + | 1064 | fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) + |
@@ -1093,6 +1238,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
1093 | goto err_req_region; | 1238 | goto err_req_region; |
1094 | } | 1239 | } |
1095 | 1240 | ||
1241 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
1242 | if (!res) { | ||
1243 | dev_err(dev, "failed to acquire irq resource\n"); | ||
1244 | ret = -ENOENT; | ||
1245 | goto err_ioremap; | ||
1246 | } | ||
1247 | sfb->irq_no = res->start; | ||
1248 | ret = request_irq(sfb->irq_no, s3c_fb_irq, | ||
1249 | 0, "s3c_fb", sfb); | ||
1250 | if (ret) { | ||
1251 | dev_err(dev, "irq request failed\n"); | ||
1252 | goto err_ioremap; | ||
1253 | } | ||
1254 | |||
1096 | dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); | 1255 | dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); |
1097 | 1256 | ||
1098 | /* setup gpio and output polarity controls */ | 1257 | /* setup gpio and output polarity controls */ |
@@ -1127,7 +1286,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
1127 | dev_err(dev, "failed to create window %d\n", win); | 1286 | dev_err(dev, "failed to create window %d\n", win); |
1128 | for (; win >= 0; win--) | 1287 | for (; win >= 0; win--) |
1129 | s3c_fb_release_win(sfb, sfb->windows[win]); | 1288 | s3c_fb_release_win(sfb, sfb->windows[win]); |
1130 | goto err_ioremap; | 1289 | goto err_irq; |
1131 | } | 1290 | } |
1132 | } | 1291 | } |
1133 | 1292 | ||
@@ -1135,6 +1294,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
1135 | 1294 | ||
1136 | return 0; | 1295 | return 0; |
1137 | 1296 | ||
1297 | err_irq: | ||
1298 | free_irq(sfb->irq_no, sfb); | ||
1299 | |||
1138 | err_ioremap: | 1300 | err_ioremap: |
1139 | iounmap(sfb->regs); | 1301 | iounmap(sfb->regs); |
1140 | 1302 | ||
@@ -1167,6 +1329,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) | |||
1167 | if (sfb->windows[win]) | 1329 | if (sfb->windows[win]) |
1168 | s3c_fb_release_win(sfb, sfb->windows[win]); | 1330 | s3c_fb_release_win(sfb, sfb->windows[win]); |
1169 | 1331 | ||
1332 | free_irq(sfb->irq_no, sfb); | ||
1333 | |||
1170 | iounmap(sfb->regs); | 1334 | iounmap(sfb->regs); |
1171 | 1335 | ||
1172 | clk_disable(sfb->bus_clk); | 1336 | clk_disable(sfb->bus_clk); |