]>
Commit | Line | Data |
---|---|---|
990a4007 AT |
1 | /* |
2 | * Copyright (c) 2016, The Linux Foundation. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU General Public License version 2 as published by | |
6 | * the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along with | |
14 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include <linux/irqdomain.h> | |
18 | #include <linux/irq.h> | |
19 | ||
20 | #include "msm_drv.h" | |
21 | #include "mdp5_kms.h" | |
22 | ||
23 | /* | |
24 | * If needed, this can become more specific: something like struct mdp5_mdss, | |
25 | * which contains a 'struct msm_mdss base' member. | |
26 | */ | |
27 | struct msm_mdss { | |
28 | struct drm_device *dev; | |
29 | ||
30 | void __iomem *mmio, *vbif; | |
31 | ||
32 | struct regulator *vdd; | |
33 | ||
34 | struct { | |
35 | volatile unsigned long enabled_mask; | |
36 | struct irq_domain *domain; | |
37 | } irqcontroller; | |
38 | }; | |
39 | ||
40 | static inline void mdss_write(struct msm_mdss *mdss, u32 reg, u32 data) | |
41 | { | |
42 | msm_writel(data, mdss->mmio + reg); | |
43 | } | |
44 | ||
45 | static inline u32 mdss_read(struct msm_mdss *mdss, u32 reg) | |
46 | { | |
47 | return msm_readl(mdss->mmio + reg); | |
48 | } | |
49 | ||
50 | static irqreturn_t mdss_irq(int irq, void *arg) | |
51 | { | |
52 | struct msm_mdss *mdss = arg; | |
53 | u32 intr; | |
54 | ||
55 | intr = mdss_read(mdss, REG_MDSS_HW_INTR_STATUS); | |
56 | ||
57 | VERB("intr=%08x", intr); | |
58 | ||
59 | while (intr) { | |
60 | irq_hw_number_t hwirq = fls(intr) - 1; | |
61 | ||
62 | generic_handle_irq(irq_find_mapping( | |
63 | mdss->irqcontroller.domain, hwirq)); | |
64 | intr &= ~(1 << hwirq); | |
65 | } | |
66 | ||
67 | return IRQ_HANDLED; | |
68 | } | |
69 | ||
70 | /* | |
71 | * interrupt-controller implementation, so sub-blocks (MDP/HDMI/eDP/DSI/etc) | |
72 | * can register to get their irq's delivered | |
73 | */ | |
74 | ||
75 | #define VALID_IRQS (MDSS_HW_INTR_STATUS_INTR_MDP | \ | |
76 | MDSS_HW_INTR_STATUS_INTR_DSI0 | \ | |
77 | MDSS_HW_INTR_STATUS_INTR_DSI1 | \ | |
78 | MDSS_HW_INTR_STATUS_INTR_HDMI | \ | |
79 | MDSS_HW_INTR_STATUS_INTR_EDP) | |
80 | ||
81 | static void mdss_hw_mask_irq(struct irq_data *irqd) | |
82 | { | |
83 | struct msm_mdss *mdss = irq_data_get_irq_chip_data(irqd); | |
84 | ||
85 | smp_mb__before_atomic(); | |
86 | clear_bit(irqd->hwirq, &mdss->irqcontroller.enabled_mask); | |
87 | smp_mb__after_atomic(); | |
88 | } | |
89 | ||
90 | static void mdss_hw_unmask_irq(struct irq_data *irqd) | |
91 | { | |
92 | struct msm_mdss *mdss = irq_data_get_irq_chip_data(irqd); | |
93 | ||
94 | smp_mb__before_atomic(); | |
95 | set_bit(irqd->hwirq, &mdss->irqcontroller.enabled_mask); | |
96 | smp_mb__after_atomic(); | |
97 | } | |
98 | ||
99 | static struct irq_chip mdss_hw_irq_chip = { | |
100 | .name = "mdss", | |
101 | .irq_mask = mdss_hw_mask_irq, | |
102 | .irq_unmask = mdss_hw_unmask_irq, | |
103 | }; | |
104 | ||
105 | static int mdss_hw_irqdomain_map(struct irq_domain *d, unsigned int irq, | |
106 | irq_hw_number_t hwirq) | |
107 | { | |
108 | struct msm_mdss *mdss = d->host_data; | |
109 | ||
110 | if (!(VALID_IRQS & (1 << hwirq))) | |
111 | return -EPERM; | |
112 | ||
113 | irq_set_chip_and_handler(irq, &mdss_hw_irq_chip, handle_level_irq); | |
114 | irq_set_chip_data(irq, mdss); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
c43dd227 | 119 | static const struct irq_domain_ops mdss_hw_irqdomain_ops = { |
990a4007 AT |
120 | .map = mdss_hw_irqdomain_map, |
121 | .xlate = irq_domain_xlate_onecell, | |
122 | }; | |
123 | ||
124 | ||
125 | static int mdss_irq_domain_init(struct msm_mdss *mdss) | |
126 | { | |
127 | struct device *dev = mdss->dev->dev; | |
128 | struct irq_domain *d; | |
129 | ||
130 | d = irq_domain_add_linear(dev->of_node, 32, &mdss_hw_irqdomain_ops, | |
131 | mdss); | |
132 | if (!d) { | |
133 | dev_err(dev, "mdss irq domain add failed\n"); | |
134 | return -ENXIO; | |
135 | } | |
136 | ||
137 | mdss->irqcontroller.enabled_mask = 0; | |
138 | mdss->irqcontroller.domain = d; | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | void msm_mdss_destroy(struct drm_device *dev) | |
144 | { | |
145 | struct msm_drm_private *priv = dev->dev_private; | |
146 | struct msm_mdss *mdss = priv->mdss; | |
147 | ||
148 | if (!mdss) | |
149 | return; | |
150 | ||
151 | irq_domain_remove(mdss->irqcontroller.domain); | |
152 | mdss->irqcontroller.domain = NULL; | |
153 | ||
154 | regulator_disable(mdss->vdd); | |
cd792726 AT |
155 | |
156 | pm_runtime_put_sync(dev->dev); | |
157 | ||
158 | pm_runtime_disable(dev->dev); | |
990a4007 AT |
159 | } |
160 | ||
161 | int msm_mdss_init(struct drm_device *dev) | |
162 | { | |
76adb460 | 163 | struct platform_device *pdev = to_platform_device(dev->dev); |
990a4007 AT |
164 | struct msm_drm_private *priv = dev->dev_private; |
165 | struct msm_mdss *mdss; | |
166 | int ret; | |
167 | ||
168 | DBG(""); | |
169 | ||
170 | if (!of_device_is_compatible(dev->dev->of_node, "qcom,mdss")) | |
171 | return 0; | |
172 | ||
173 | mdss = devm_kzalloc(dev->dev, sizeof(*mdss), GFP_KERNEL); | |
174 | if (!mdss) { | |
175 | ret = -ENOMEM; | |
176 | goto fail; | |
177 | } | |
178 | ||
179 | mdss->dev = dev; | |
180 | ||
181 | mdss->mmio = msm_ioremap(pdev, "mdss_phys", "MDSS"); | |
182 | if (IS_ERR(mdss->mmio)) { | |
183 | ret = PTR_ERR(mdss->mmio); | |
184 | goto fail; | |
185 | } | |
186 | ||
187 | mdss->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF"); | |
188 | if (IS_ERR(mdss->vbif)) { | |
189 | ret = PTR_ERR(mdss->vbif); | |
190 | goto fail; | |
191 | } | |
192 | ||
193 | /* Regulator to enable GDSCs in downstream kernels */ | |
194 | mdss->vdd = devm_regulator_get(dev->dev, "vdd"); | |
195 | if (IS_ERR(mdss->vdd)) { | |
196 | ret = PTR_ERR(mdss->vdd); | |
197 | goto fail; | |
198 | } | |
199 | ||
200 | ret = regulator_enable(mdss->vdd); | |
201 | if (ret) { | |
202 | dev_err(dev->dev, "failed to enable regulator vdd: %d\n", | |
203 | ret); | |
204 | goto fail; | |
205 | } | |
206 | ||
207 | ret = devm_request_irq(dev->dev, platform_get_irq(pdev, 0), | |
208 | mdss_irq, 0, "mdss_isr", mdss); | |
209 | if (ret) { | |
210 | dev_err(dev->dev, "failed to init irq: %d\n", ret); | |
211 | goto fail_irq; | |
212 | } | |
213 | ||
214 | ret = mdss_irq_domain_init(mdss); | |
215 | if (ret) { | |
216 | dev_err(dev->dev, "failed to init sub-block irqs: %d\n", ret); | |
217 | goto fail_irq; | |
218 | } | |
219 | ||
220 | priv->mdss = mdss; | |
221 | ||
cd792726 AT |
222 | pm_runtime_enable(dev->dev); |
223 | ||
224 | /* | |
225 | * TODO: This is needed as the MDSS GDSC is only tied to MDSS's power | |
226 | * domain. Remove this once runtime PM is adapted for all the devices. | |
227 | */ | |
228 | pm_runtime_get_sync(dev->dev); | |
229 | ||
990a4007 AT |
230 | return 0; |
231 | fail_irq: | |
232 | regulator_disable(mdss->vdd); | |
233 | fail: | |
234 | return ret; | |
235 | } |