diff options
| -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 | ||||
| -rw-r--r-- | include/video/sh_mobile_lcdc.h | 2 |
4 files changed, 141 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 | |||
