diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2010-09-03 03:20:23 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-09-14 04:23:21 -0400 |
commit | 6de9edd5bde0cdfea12e9948690e53ec669c3018 (patch) | |
tree | 638602a3d7726b27ae6ab85ef45f4f11c38c0283 /drivers/video | |
parent | 89712699d7bc9cc93602407e0e9bc2490b771400 (diff) |
fbdev: sh_mobile_hdmi: implement locking
The SH-Mobile HDMI driver runs in several contexts: ISR, delayed work-queue,
task context, when called from the sh_mobile_lcdc framebuffer driver. This
creates ample race possibilities. Even though most these races are purely
theoretical, it is better to close them. To trace fb_info validity we install a
notification callback in the HDMI driver, and the only way for it to get to
driver internal data is by using struct sh_mobile_lcdc_chan, therefore it had
to be extracted into a separate common header.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 103 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 46 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.h | 37 |
3 files changed, 139 insertions, 47 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index a8cf9a510f30..56e44fd0a3af 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include <video/sh_mobile_hdmi.h> | 26 | #include <video/sh_mobile_hdmi.h> |
27 | #include <video/sh_mobile_lcdc.h> | 27 | #include <video/sh_mobile_lcdc.h> |
28 | 28 | ||
29 | #include "sh_mobile_lcdcfb.h" | ||
30 | |||
29 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ | 31 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ |
30 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, | 32 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, |
31 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ | 33 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ |
@@ -209,6 +211,7 @@ struct sh_hdmi { | |||
209 | struct clk *hdmi_clk; | 211 | struct clk *hdmi_clk; |
210 | struct device *dev; | 212 | struct device *dev; |
211 | struct fb_info *info; | 213 | struct fb_info *info; |
214 | struct mutex mutex; /* Protect the info pointer */ | ||
212 | struct delayed_work edid_work; | 215 | struct delayed_work edid_work; |
213 | struct fb_var_screeninfo var; | 216 | struct fb_var_screeninfo var; |
214 | }; | 217 | }; |
@@ -720,17 +723,21 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
720 | return IRQ_HANDLED; | 723 | return IRQ_HANDLED; |
721 | } | 724 | } |
722 | 725 | ||
726 | /* locking: called with info->lock held, or before register_framebuffer() */ | ||
723 | static void hdmi_display_on(void *arg, struct fb_info *info) | 727 | static void hdmi_display_on(void *arg, struct fb_info *info) |
724 | { | 728 | { |
729 | /* | ||
730 | * info is guaranteed to be valid, when we are called, because our | ||
731 | * FB_EVENT_FB_UNBIND notify is also called with info->lock held | ||
732 | */ | ||
725 | struct sh_hdmi *hdmi = arg; | 733 | struct sh_hdmi *hdmi = arg; |
726 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 734 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
727 | 735 | ||
728 | pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); | 736 | pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); |
729 | /* | 737 | |
730 | * FIXME: not a good place to store fb_info. And we cannot nullify it | 738 | /* No need to lock */ |
731 | * even on monitor disconnect. What should the lifecycle be? | ||
732 | */ | ||
733 | hdmi->info = info; | 739 | hdmi->info = info; |
740 | |||
734 | switch (hdmi->hp_state) { | 741 | switch (hdmi->hp_state) { |
735 | case HDMI_HOTPLUG_EDID_DONE: | 742 | case HDMI_HOTPLUG_EDID_DONE: |
736 | /* PS mode d->e. All functions are active */ | 743 | /* PS mode d->e. All functions are active */ |
@@ -744,6 +751,7 @@ static void hdmi_display_on(void *arg, struct fb_info *info) | |||
744 | } | 751 | } |
745 | } | 752 | } |
746 | 753 | ||
754 | /* locking: called with info->lock held */ | ||
747 | static void hdmi_display_off(void *arg) | 755 | static void hdmi_display_off(void *arg) |
748 | { | 756 | { |
749 | struct sh_hdmi *hdmi = arg; | 757 | struct sh_hdmi *hdmi = arg; |
@@ -766,6 +774,8 @@ static void edid_work_fn(struct work_struct *work) | |||
766 | if (!pdata->lcd_dev) | 774 | if (!pdata->lcd_dev) |
767 | return; | 775 | return; |
768 | 776 | ||
777 | mutex_lock(&hdmi->mutex); | ||
778 | |||
769 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | 779 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { |
770 | pm_runtime_get_sync(hdmi->dev); | 780 | pm_runtime_get_sync(hdmi->dev); |
771 | /* A device has been plugged in */ | 781 | /* A device has been plugged in */ |
@@ -776,21 +786,25 @@ static void edid_work_fn(struct work_struct *work) | |||
776 | msleep(10); | 786 | msleep(10); |
777 | 787 | ||
778 | if (!hdmi->info) | 788 | if (!hdmi->info) |
779 | return; | 789 | goto out; |
780 | 790 | ||
781 | acquire_console_sem(); | 791 | acquire_console_sem(); |
782 | 792 | ||
783 | /* HDMI plug in */ | 793 | /* HDMI plug in */ |
784 | hdmi->info->var = hdmi->var; | 794 | hdmi->info->var = hdmi->var; |
785 | if (hdmi->info->state != FBINFO_STATE_RUNNING) | 795 | if (hdmi->info->state != FBINFO_STATE_RUNNING) { |
786 | fb_set_suspend(hdmi->info, 0); | 796 | fb_set_suspend(hdmi->info, 0); |
787 | else | 797 | } else { |
788 | hdmi_display_on(hdmi, hdmi->info); | 798 | if (lock_fb_info(hdmi->info)) { |
799 | hdmi_display_on(hdmi, hdmi->info); | ||
800 | unlock_fb_info(hdmi->info); | ||
801 | } | ||
802 | } | ||
789 | 803 | ||
790 | release_console_sem(); | 804 | release_console_sem(); |
791 | } else { | 805 | } else { |
792 | if (!hdmi->info) | 806 | if (!hdmi->info) |
793 | return; | 807 | goto out; |
794 | 808 | ||
795 | acquire_console_sem(); | 809 | acquire_console_sem(); |
796 | 810 | ||
@@ -801,13 +815,61 @@ static void edid_work_fn(struct work_struct *work) | |||
801 | pm_runtime_put(hdmi->dev); | 815 | pm_runtime_put(hdmi->dev); |
802 | } | 816 | } |
803 | 817 | ||
818 | out: | ||
819 | mutex_unlock(&hdmi->mutex); | ||
820 | |||
804 | pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); | 821 | pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); |
805 | } | 822 | } |
806 | 823 | ||
824 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
825 | unsigned long action, void *data); | ||
826 | |||
827 | static struct notifier_block sh_hdmi_notifier = { | ||
828 | .notifier_call = sh_hdmi_notify, | ||
829 | }; | ||
830 | |||
831 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
832 | unsigned long action, void *data) | ||
833 | { | ||
834 | struct fb_event *event = data; | ||
835 | struct fb_info *info = event->info; | ||
836 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
837 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | ||
838 | struct sh_hdmi *hdmi = board_cfg->board_data; | ||
839 | |||
840 | if (nb != &sh_hdmi_notifier || !hdmi || hdmi->info != info) | ||
841 | return NOTIFY_DONE; | ||
842 | |||
843 | switch(action) { | ||
844 | case FB_EVENT_FB_REGISTERED: | ||
845 | /* Unneeded, activation taken care by hdmi_display_on() */ | ||
846 | break; | ||
847 | case FB_EVENT_FB_UNREGISTERED: | ||
848 | /* | ||
849 | * We are called from unregister_framebuffer() with the | ||
850 | * info->lock held. This is bad for us, because we can race with | ||
851 | * the scheduled work, which has to call fb_set_suspend(), which | ||
852 | * takes info->lock internally, so, edid_work_fn() cannot take | ||
853 | * and hold info->lock for the whole function duration. Using an | ||
854 | * additional lock creates a classical AB-BA lock up. Therefore, | ||
855 | * we have to release the info->lock temporarily, synchronise | ||
856 | * with the work queue and re-acquire the info->lock. | ||
857 | */ | ||
858 | unlock_fb_info(hdmi->info); | ||
859 | mutex_lock(&hdmi->mutex); | ||
860 | hdmi->info = NULL; | ||
861 | mutex_unlock(&hdmi->mutex); | ||
862 | lock_fb_info(hdmi->info); | ||
863 | return NOTIFY_OK; | ||
864 | } | ||
865 | return NOTIFY_DONE; | ||
866 | } | ||
867 | |||
807 | static int __init sh_hdmi_probe(struct platform_device *pdev) | 868 | static int __init sh_hdmi_probe(struct platform_device *pdev) |
808 | { | 869 | { |
809 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 870 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
810 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 871 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
872 | struct sh_mobile_lcdc_board_cfg *board_cfg; | ||
811 | int irq = platform_get_irq(pdev, 0), ret; | 873 | int irq = platform_get_irq(pdev, 0), ret; |
812 | struct sh_hdmi *hdmi; | 874 | struct sh_hdmi *hdmi; |
813 | long rate; | 875 | long rate; |
@@ -821,6 +883,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
821 | return -ENOMEM; | 883 | return -ENOMEM; |
822 | } | 884 | } |
823 | 885 | ||
886 | mutex_init(&hdmi->mutex); | ||
824 | hdmi->dev = &pdev->dev; | 887 | hdmi->dev = &pdev->dev; |
825 | 888 | ||
826 | hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); | 889 | hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); |
@@ -878,9 +941,11 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
878 | #endif | 941 | #endif |
879 | 942 | ||
880 | /* Set up LCDC callbacks */ | 943 | /* Set up LCDC callbacks */ |
881 | pdata->lcd_chan->board_cfg.board_data = hdmi; | 944 | board_cfg = &pdata->lcd_chan->board_cfg; |
882 | pdata->lcd_chan->board_cfg.display_on = hdmi_display_on; | 945 | board_cfg->owner = THIS_MODULE; |
883 | pdata->lcd_chan->board_cfg.display_off = hdmi_display_off; | 946 | board_cfg->board_data = hdmi; |
947 | board_cfg->display_on = hdmi_display_on; | ||
948 | board_cfg->display_off = hdmi_display_off; | ||
884 | 949 | ||
885 | INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); | 950 | INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); |
886 | 951 | ||
@@ -907,6 +972,7 @@ eclkenable: | |||
907 | erate: | 972 | erate: |
908 | clk_put(hdmi->hdmi_clk); | 973 | clk_put(hdmi->hdmi_clk); |
909 | egetclk: | 974 | egetclk: |
975 | mutex_destroy(&hdmi->mutex); | ||
910 | kfree(hdmi); | 976 | kfree(hdmi); |
911 | 977 | ||
912 | return ret; | 978 | return ret; |
@@ -917,19 +983,24 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) | |||
917 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 983 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
918 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); | 984 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); |
919 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 985 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
986 | struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; | ||
920 | int irq = platform_get_irq(pdev, 0); | 987 | int irq = platform_get_irq(pdev, 0); |
921 | 988 | ||
922 | pdata->lcd_chan->board_cfg.display_on = NULL; | 989 | board_cfg->display_on = NULL; |
923 | pdata->lcd_chan->board_cfg.display_off = NULL; | 990 | board_cfg->display_off = NULL; |
924 | pdata->lcd_chan->board_cfg.board_data = NULL; | 991 | board_cfg->board_data = NULL; |
992 | board_cfg->owner = NULL; | ||
925 | 993 | ||
994 | /* No new work will be scheduled, wait for running ISR */ | ||
926 | free_irq(irq, hdmi); | 995 | free_irq(irq, hdmi); |
927 | pm_runtime_disable(&pdev->dev); | 996 | /* Wait for already scheduled work */ |
928 | cancel_delayed_work_sync(&hdmi->edid_work); | 997 | cancel_delayed_work_sync(&hdmi->edid_work); |
998 | pm_runtime_disable(&pdev->dev); | ||
929 | clk_disable(hdmi->hdmi_clk); | 999 | clk_disable(hdmi->hdmi_clk); |
930 | clk_put(hdmi->hdmi_clk); | 1000 | clk_put(hdmi->hdmi_clk); |
931 | iounmap(hdmi->base); | 1001 | iounmap(hdmi->base); |
932 | release_mem_region(res->start, resource_size(res)); | 1002 | release_mem_region(res->start, resource_size(res)); |
1003 | mutex_destroy(&hdmi->mutex); | ||
933 | kfree(hdmi); | 1004 | kfree(hdmi); |
934 | 1005 | ||
935 | return 0; | 1006 | return 0; |
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index ddd6c4669bd7..29d7ce7e7a1c 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
@@ -12,7 +12,6 @@ | |||
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <linux/mm.h> | 14 | #include <linux/mm.h> |
15 | #include <linux/fb.h> | ||
16 | #include <linux/clk.h> | 15 | #include <linux/clk.h> |
17 | #include <linux/pm_runtime.h> | 16 | #include <linux/pm_runtime.h> |
18 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
@@ -24,7 +23,8 @@ | |||
24 | #include <video/sh_mobile_lcdc.h> | 23 | #include <video/sh_mobile_lcdc.h> |
25 | #include <asm/atomic.h> | 24 | #include <asm/atomic.h> |
26 | 25 | ||
27 | #define PALETTE_NR 16 | 26 | #include "sh_mobile_lcdcfb.h" |
27 | |||
28 | #define SIDE_B_OFFSET 0x1000 | 28 | #define SIDE_B_OFFSET 0x1000 |
29 | #define MIRROR_OFFSET 0x2000 | 29 | #define MIRROR_OFFSET 0x2000 |
30 | 30 | ||
@@ -53,12 +53,6 @@ static int lcdc_shared_regs[] = { | |||
53 | }; | 53 | }; |
54 | #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) | 54 | #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) |
55 | 55 | ||
56 | /* per-channel registers */ | ||
57 | enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, | ||
58 | LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, | ||
59 | LDHAJR, | ||
60 | NR_CH_REGS }; | ||
61 | |||
62 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { | 56 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { |
63 | [LDDCKPAT1R] = 0x400, | 57 | [LDDCKPAT1R] = 0x400, |
64 | [LDDCKPAT2R] = 0x404, | 58 | [LDDCKPAT2R] = 0x404, |
@@ -112,25 +106,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { | |||
112 | #define LDRCNTR_MRC 0x00000001 | 106 | #define LDRCNTR_MRC 0x00000001 |
113 | #define LDSR_MRS 0x00000100 | 107 | #define LDSR_MRS 0x00000100 |
114 | 108 | ||
115 | struct sh_mobile_lcdc_priv; | ||
116 | struct sh_mobile_lcdc_chan { | ||
117 | struct sh_mobile_lcdc_priv *lcdc; | ||
118 | unsigned long *reg_offs; | ||
119 | unsigned long ldmt1r_value; | ||
120 | unsigned long enabled; /* ME and SE in LDCNT2R */ | ||
121 | struct sh_mobile_lcdc_chan_cfg cfg; | ||
122 | u32 pseudo_palette[PALETTE_NR]; | ||
123 | unsigned long saved_ch_regs[NR_CH_REGS]; | ||
124 | struct fb_info *info; | ||
125 | dma_addr_t dma_handle; | ||
126 | struct fb_deferred_io defio; | ||
127 | struct scatterlist *sglist; | ||
128 | unsigned long frame_end; | ||
129 | unsigned long pan_offset; | ||
130 | wait_queue_head_t frame_end_wait; | ||
131 | struct completion vsync_completion; | ||
132 | }; | ||
133 | |||
134 | struct sh_mobile_lcdc_priv { | 109 | struct sh_mobile_lcdc_priv { |
135 | void __iomem *base; | 110 | void __iomem *base; |
136 | int irq; | 111 | int irq; |
@@ -589,8 +564,10 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
589 | continue; | 564 | continue; |
590 | 565 | ||
591 | board_cfg = &ch->cfg.board_cfg; | 566 | board_cfg = &ch->cfg.board_cfg; |
592 | if (board_cfg->display_on) | 567 | if (try_module_get(board_cfg->owner) && board_cfg->display_on) { |
593 | board_cfg->display_on(board_cfg->board_data, ch->info); | 568 | board_cfg->display_on(board_cfg->board_data, ch->info); |
569 | module_put(board_cfg->owner); | ||
570 | } | ||
594 | } | 571 | } |
595 | 572 | ||
596 | return 0; | 573 | return 0; |
@@ -622,8 +599,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
622 | } | 599 | } |
623 | 600 | ||
624 | board_cfg = &ch->cfg.board_cfg; | 601 | board_cfg = &ch->cfg.board_cfg; |
625 | if (board_cfg->display_off) | 602 | if (try_module_get(board_cfg->owner) && board_cfg->display_off) { |
626 | board_cfg->display_off(board_cfg->board_data); | 603 | board_cfg->display_off(board_cfg->board_data); |
604 | module_put(board_cfg->owner); | ||
605 | } | ||
627 | } | 606 | } |
628 | 607 | ||
629 | /* stop the lcdc */ | 608 | /* stop the lcdc */ |
@@ -954,6 +933,7 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { | |||
954 | .runtime_resume = sh_mobile_lcdc_runtime_resume, | 933 | .runtime_resume = sh_mobile_lcdc_runtime_resume, |
955 | }; | 934 | }; |
956 | 935 | ||
936 | /* locking: called with info->lock held */ | ||
957 | static int sh_mobile_lcdc_notify(struct notifier_block *nb, | 937 | static int sh_mobile_lcdc_notify(struct notifier_block *nb, |
958 | unsigned long action, void *data) | 938 | unsigned long action, void *data) |
959 | { | 939 | { |
@@ -971,16 +951,20 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, | |||
971 | 951 | ||
972 | switch(action) { | 952 | switch(action) { |
973 | case FB_EVENT_SUSPEND: | 953 | case FB_EVENT_SUSPEND: |
974 | if (board_cfg->display_off) | 954 | if (try_module_get(board_cfg->owner) && board_cfg->display_off) { |
975 | board_cfg->display_off(board_cfg->board_data); | 955 | board_cfg->display_off(board_cfg->board_data); |
956 | module_put(board_cfg->owner); | ||
957 | } | ||
976 | pm_runtime_put(info->device); | 958 | pm_runtime_put(info->device); |
977 | break; | 959 | break; |
978 | case FB_EVENT_RESUME: | 960 | case FB_EVENT_RESUME: |
979 | var = &info->var; | 961 | var = &info->var; |
980 | 962 | ||
981 | /* HDMI must be enabled before LCDC configuration */ | 963 | /* HDMI must be enabled before LCDC configuration */ |
982 | if (board_cfg->display_on) | 964 | if (try_module_get(board_cfg->owner) && board_cfg->display_on) { |
983 | board_cfg->display_on(board_cfg->board_data, ch->info); | 965 | board_cfg->display_on(board_cfg->board_data, ch->info); |
966 | module_put(board_cfg->owner); | ||
967 | } | ||
984 | 968 | ||
985 | /* Check if the new display is not in our modelist */ | 969 | /* Check if the new display is not in our modelist */ |
986 | if (ch->info->modelist.next && | 970 | if (ch->info->modelist.next && |
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h new file mode 100644 index 000000000000..6fcfc0ffe3f8 --- /dev/null +++ b/drivers/video/sh_mobile_lcdcfb.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #ifndef SH_MOBILE_LCDCFB_H | ||
2 | #define SH_MOBILE_LCDCFB_H | ||
3 | |||
4 | #include <linux/completion.h> | ||
5 | #include <linux/fb.h> | ||
6 | #include <linux/wait.h> | ||
7 | |||
8 | /* per-channel registers */ | ||
9 | enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, | ||
10 | LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, | ||
11 | LDHAJR, | ||
12 | NR_CH_REGS }; | ||
13 | |||
14 | #define PALETTE_NR 16 | ||
15 | |||
16 | struct sh_mobile_lcdc_priv; | ||
17 | struct fb_info; | ||
18 | |||
19 | struct sh_mobile_lcdc_chan { | ||
20 | struct sh_mobile_lcdc_priv *lcdc; | ||
21 | unsigned long *reg_offs; | ||
22 | unsigned long ldmt1r_value; | ||
23 | unsigned long enabled; /* ME and SE in LDCNT2R */ | ||
24 | struct sh_mobile_lcdc_chan_cfg cfg; | ||
25 | u32 pseudo_palette[PALETTE_NR]; | ||
26 | unsigned long saved_ch_regs[NR_CH_REGS]; | ||
27 | struct fb_info *info; | ||
28 | dma_addr_t dma_handle; | ||
29 | struct fb_deferred_io defio; | ||
30 | struct scatterlist *sglist; | ||
31 | unsigned long frame_end; | ||
32 | unsigned long pan_offset; | ||
33 | wait_queue_head_t frame_end_wait; | ||
34 | struct completion vsync_completion; | ||
35 | }; | ||
36 | |||
37 | #endif | ||