aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2014-04-11 06:49:55 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2014-04-14 07:52:08 -0400
commit0925afc9a4851c2592f1d45a17aeb7e1ffe188b7 (patch)
tree7576e5c2f5a97af7ca972d45123fad18d90a6fc3 /drivers/video
parent25e475e11d8acff1b7c2febbf1d170e95033ba2e (diff)
OMAPDSS: fix shared irq handlers
DSS uses shared irq handlers for DISPC and DSI, because on OMAP3, the DISPC and DSI share the same irq line. However, the irq handlers presume that the hardware is enabled, which, in theory, may not be the case with shared irq handlers. So if an interrupt happens while the DISPC/DSI is off, the kernel will halt as the irq handler tries to access the DISPC/DSI registers. In practice that should never happen, as both DSI and DISPC are in the same power domain. So if there's an IRQ for one of them, the other is also enabled. However, if CONFIG_DEBUG_SHIRQ is enabled, the kernel will generate a spurious IRQ, which then causes the problem. This patch adds an is_enabled field for both DISPC and DSI, which is used to track if the HW is enabled. For DISPC the code is slightly more complex, as the users of DISPC can register the interrupt handler, and we want to hide the is_enabled handling from the users of DISPC. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/omap2/dss/dispc.c55
-rw-r--r--drivers/video/omap2/dss/dsi.c20
2 files changed, 68 insertions, 7 deletions
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 2bbdb7ff7daf..b37e3fbf60cc 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -101,6 +101,8 @@ static struct {
101 void __iomem *base; 101 void __iomem *base;
102 102
103 int irq; 103 int irq;
104 irq_handler_t user_handler;
105 void *user_data;
104 106
105 unsigned long core_clk_rate; 107 unsigned long core_clk_rate;
106 unsigned long tv_pclk_rate; 108 unsigned long tv_pclk_rate;
@@ -113,6 +115,8 @@ static struct {
113 u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; 115 u32 ctx[DISPC_SZ_REGS / sizeof(u32)];
114 116
115 const struct dispc_features *feat; 117 const struct dispc_features *feat;
118
119 bool is_enabled;
116} dispc; 120} dispc;
117 121
118enum omap_color_component { 122enum omap_color_component {
@@ -3669,16 +3673,44 @@ static int __init dispc_init_features(struct platform_device *pdev)
3669 return 0; 3673 return 0;
3670} 3674}
3671 3675
3676static irqreturn_t dispc_irq_handler(int irq, void *arg)
3677{
3678 if (!dispc.is_enabled)
3679 return IRQ_NONE;
3680
3681 return dispc.user_handler(irq, dispc.user_data);
3682}
3683
3672int dispc_request_irq(irq_handler_t handler, void *dev_id) 3684int dispc_request_irq(irq_handler_t handler, void *dev_id)
3673{ 3685{
3674 return devm_request_irq(&dispc.pdev->dev, dispc.irq, handler, 3686 int r;
3675 IRQF_SHARED, "OMAP DISPC", dev_id); 3687
3688 if (dispc.user_handler != NULL)
3689 return -EBUSY;
3690
3691 dispc.user_handler = handler;
3692 dispc.user_data = dev_id;
3693
3694 /* ensure the dispc_irq_handler sees the values above */
3695 smp_wmb();
3696
3697 r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler,
3698 IRQF_SHARED, "OMAP DISPC", &dispc);
3699 if (r) {
3700 dispc.user_handler = NULL;
3701 dispc.user_data = NULL;
3702 }
3703
3704 return r;
3676} 3705}
3677EXPORT_SYMBOL(dispc_request_irq); 3706EXPORT_SYMBOL(dispc_request_irq);
3678 3707
3679void dispc_free_irq(void *dev_id) 3708void dispc_free_irq(void *dev_id)
3680{ 3709{
3681 devm_free_irq(&dispc.pdev->dev, dispc.irq, dev_id); 3710 devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc);
3711
3712 dispc.user_handler = NULL;
3713 dispc.user_data = NULL;
3682} 3714}
3683EXPORT_SYMBOL(dispc_free_irq); 3715EXPORT_SYMBOL(dispc_free_irq);
3684 3716
@@ -3750,6 +3782,12 @@ static int __exit omap_dispchw_remove(struct platform_device *pdev)
3750 3782
3751static int dispc_runtime_suspend(struct device *dev) 3783static int dispc_runtime_suspend(struct device *dev)
3752{ 3784{
3785 dispc.is_enabled = false;
3786 /* ensure the dispc_irq_handler sees the is_enabled value */
3787 smp_wmb();
3788 /* wait for current handler to finish before turning the DISPC off */
3789 synchronize_irq(dispc.irq);
3790
3753 dispc_save_context(); 3791 dispc_save_context();
3754 3792
3755 return 0; 3793 return 0;
@@ -3763,12 +3801,15 @@ static int dispc_runtime_resume(struct device *dev)
3763 * _omap_dispc_initial_config(). We can thus use it to detect if 3801 * _omap_dispc_initial_config(). We can thus use it to detect if
3764 * we have lost register context. 3802 * we have lost register context.
3765 */ 3803 */
3766 if (REG_GET(DISPC_CONFIG, 2, 1) == OMAP_DSS_LOAD_FRAME_ONLY) 3804 if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) {
3767 return 0; 3805 _omap_dispc_initial_config();
3768 3806
3769 _omap_dispc_initial_config(); 3807 dispc_restore_context();
3808 }
3770 3809
3771 dispc_restore_context(); 3810 dispc.is_enabled = true;
3811 /* ensure the dispc_irq_handler sees the is_enabled value */
3812 smp_wmb();
3772 3813
3773 return 0; 3814 return 0;
3774} 3815}
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 121d1049d0bc..8be9b04d8849 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -297,6 +297,8 @@ struct dsi_data {
297 297
298 int irq; 298 int irq;
299 299
300 bool is_enabled;
301
300 struct clk *dss_clk; 302 struct clk *dss_clk;
301 struct clk *sys_clk; 303 struct clk *sys_clk;
302 304
@@ -795,6 +797,9 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
795 dsidev = (struct platform_device *) arg; 797 dsidev = (struct platform_device *) arg;
796 dsi = dsi_get_dsidrv_data(dsidev); 798 dsi = dsi_get_dsidrv_data(dsidev);
797 799
800 if (!dsi->is_enabled)
801 return IRQ_NONE;
802
798 spin_lock(&dsi->irq_lock); 803 spin_lock(&dsi->irq_lock);
799 804
800 irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS); 805 irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS);
@@ -5671,6 +5676,15 @@ static int __exit omap_dsihw_remove(struct platform_device *dsidev)
5671 5676
5672static int dsi_runtime_suspend(struct device *dev) 5677static int dsi_runtime_suspend(struct device *dev)
5673{ 5678{
5679 struct platform_device *pdev = to_platform_device(dev);
5680 struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
5681
5682 dsi->is_enabled = false;
5683 /* ensure the irq handler sees the is_enabled value */
5684 smp_wmb();
5685 /* wait for current handler to finish before turning the DSI off */
5686 synchronize_irq(dsi->irq);
5687
5674 dispc_runtime_put(); 5688 dispc_runtime_put();
5675 5689
5676 return 0; 5690 return 0;
@@ -5678,12 +5692,18 @@ static int dsi_runtime_suspend(struct device *dev)
5678 5692
5679static int dsi_runtime_resume(struct device *dev) 5693static int dsi_runtime_resume(struct device *dev)
5680{ 5694{
5695 struct platform_device *pdev = to_platform_device(dev);
5696 struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
5681 int r; 5697 int r;
5682 5698
5683 r = dispc_runtime_get(); 5699 r = dispc_runtime_get();
5684 if (r) 5700 if (r)
5685 return r; 5701 return r;
5686 5702
5703 dsi->is_enabled = true;
5704 /* ensure the irq handler sees the is_enabled value */
5705 smp_wmb();
5706
5687 return 0; 5707 return 0;
5688} 5708}
5689 5709