1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
3  */
4 
5 #include <linux/bitops.h>
6 #include <linux/debugfs.h>
7 #include <linux/slab.h>
8 
9 #include "dpu_core_irq.h"
10 #include "dpu_kms.h"
11 #include "dpu_hw_interrupts.h"
12 #include "dpu_hw_util.h"
13 #include "dpu_hw_mdss.h"
14 #include "dpu_trace.h"
15 
16 /*
17  * Register offsets in MDSS register file for the interrupt registers
18  * w.r.t. to the MDP base
19  */
20 #define MDP_SSPP_TOP0_OFF		0x0
21 #define MDP_INTF_0_OFF			0x6A000
22 #define MDP_INTF_1_OFF			0x6A800
23 #define MDP_INTF_2_OFF			0x6B000
24 #define MDP_INTF_3_OFF			0x6B800
25 #define MDP_INTF_4_OFF			0x6C000
26 #define MDP_INTF_5_OFF			0x6C800
27 #define MDP_AD4_0_OFF			0x7C000
28 #define MDP_AD4_1_OFF			0x7D000
29 #define MDP_AD4_INTR_EN_OFF		0x41c
30 #define MDP_AD4_INTR_CLEAR_OFF		0x424
31 #define MDP_AD4_INTR_STATUS_OFF		0x420
32 #define MDP_INTF_0_OFF_REV_7xxx             0x34000
33 #define MDP_INTF_1_OFF_REV_7xxx             0x35000
34 #define MDP_INTF_2_OFF_REV_7xxx             0x36000
35 #define MDP_INTF_3_OFF_REV_7xxx             0x37000
36 #define MDP_INTF_4_OFF_REV_7xxx             0x38000
37 #define MDP_INTF_5_OFF_REV_7xxx             0x39000
38 
39 /**
40  * struct dpu_intr_reg - array of DPU register sets
41  * @clr_off:	offset to CLEAR reg
42  * @en_off:	offset to ENABLE reg
43  * @status_off:	offset to STATUS reg
44  */
45 struct dpu_intr_reg {
46 	u32 clr_off;
47 	u32 en_off;
48 	u32 status_off;
49 };
50 
51 /*
52  * struct dpu_intr_reg -  List of DPU interrupt registers
53  *
54  * When making changes be sure to sync with dpu_hw_intr_reg
55  */
56 static const struct dpu_intr_reg dpu_intr_set[] = {
57 	{
58 		MDP_SSPP_TOP0_OFF+INTR_CLEAR,
59 		MDP_SSPP_TOP0_OFF+INTR_EN,
60 		MDP_SSPP_TOP0_OFF+INTR_STATUS
61 	},
62 	{
63 		MDP_SSPP_TOP0_OFF+INTR2_CLEAR,
64 		MDP_SSPP_TOP0_OFF+INTR2_EN,
65 		MDP_SSPP_TOP0_OFF+INTR2_STATUS
66 	},
67 	{
68 		MDP_SSPP_TOP0_OFF+HIST_INTR_CLEAR,
69 		MDP_SSPP_TOP0_OFF+HIST_INTR_EN,
70 		MDP_SSPP_TOP0_OFF+HIST_INTR_STATUS
71 	},
72 	{
73 		MDP_INTF_0_OFF+INTF_INTR_CLEAR,
74 		MDP_INTF_0_OFF+INTF_INTR_EN,
75 		MDP_INTF_0_OFF+INTF_INTR_STATUS
76 	},
77 	{
78 		MDP_INTF_1_OFF+INTF_INTR_CLEAR,
79 		MDP_INTF_1_OFF+INTF_INTR_EN,
80 		MDP_INTF_1_OFF+INTF_INTR_STATUS
81 	},
82 	{
83 		MDP_INTF_2_OFF+INTF_INTR_CLEAR,
84 		MDP_INTF_2_OFF+INTF_INTR_EN,
85 		MDP_INTF_2_OFF+INTF_INTR_STATUS
86 	},
87 	{
88 		MDP_INTF_3_OFF+INTF_INTR_CLEAR,
89 		MDP_INTF_3_OFF+INTF_INTR_EN,
90 		MDP_INTF_3_OFF+INTF_INTR_STATUS
91 	},
92 	{
93 		MDP_INTF_4_OFF+INTF_INTR_CLEAR,
94 		MDP_INTF_4_OFF+INTF_INTR_EN,
95 		MDP_INTF_4_OFF+INTF_INTR_STATUS
96 	},
97 	{
98 		MDP_INTF_5_OFF+INTF_INTR_CLEAR,
99 		MDP_INTF_5_OFF+INTF_INTR_EN,
100 		MDP_INTF_5_OFF+INTF_INTR_STATUS
101 	},
102 	{
103 		MDP_AD4_0_OFF + MDP_AD4_INTR_CLEAR_OFF,
104 		MDP_AD4_0_OFF + MDP_AD4_INTR_EN_OFF,
105 		MDP_AD4_0_OFF + MDP_AD4_INTR_STATUS_OFF,
106 	},
107 	{
108 		MDP_AD4_1_OFF + MDP_AD4_INTR_CLEAR_OFF,
109 		MDP_AD4_1_OFF + MDP_AD4_INTR_EN_OFF,
110 		MDP_AD4_1_OFF + MDP_AD4_INTR_STATUS_OFF,
111 	},
112 	{
113 		MDP_INTF_0_OFF_REV_7xxx+INTF_INTR_CLEAR,
114 		MDP_INTF_0_OFF_REV_7xxx+INTF_INTR_EN,
115 		MDP_INTF_0_OFF_REV_7xxx+INTF_INTR_STATUS
116 	},
117 	{
118 		MDP_INTF_1_OFF_REV_7xxx+INTF_INTR_CLEAR,
119 		MDP_INTF_1_OFF_REV_7xxx+INTF_INTR_EN,
120 		MDP_INTF_1_OFF_REV_7xxx+INTF_INTR_STATUS
121 	},
122 	{
123 		MDP_INTF_2_OFF_REV_7xxx+INTF_INTR_CLEAR,
124 		MDP_INTF_2_OFF_REV_7xxx+INTF_INTR_EN,
125 		MDP_INTF_2_OFF_REV_7xxx+INTF_INTR_STATUS
126 	},
127 	{
128 		MDP_INTF_3_OFF_REV_7xxx+INTF_INTR_CLEAR,
129 		MDP_INTF_3_OFF_REV_7xxx+INTF_INTR_EN,
130 		MDP_INTF_3_OFF_REV_7xxx+INTF_INTR_STATUS
131 	},
132 	{
133 		MDP_INTF_4_OFF_REV_7xxx+INTF_INTR_CLEAR,
134 		MDP_INTF_4_OFF_REV_7xxx+INTF_INTR_EN,
135 		MDP_INTF_4_OFF_REV_7xxx+INTF_INTR_STATUS
136 	},
137 	{
138 		MDP_INTF_5_OFF_REV_7xxx+INTF_INTR_CLEAR,
139 		MDP_INTF_5_OFF_REV_7xxx+INTF_INTR_EN,
140 		MDP_INTF_5_OFF_REV_7xxx+INTF_INTR_STATUS
141 	},
142 };
143 
144 #define DPU_IRQ_REG(irq_idx)	(irq_idx / 32)
145 #define DPU_IRQ_MASK(irq_idx)	(BIT(irq_idx % 32))
146 
147 /**
148  * dpu_core_irq_callback_handler - dispatch core interrupts
149  * @dpu_kms:		Pointer to DPU's KMS structure
150  * @irq_idx:		interrupt index
151  */
152 static void dpu_core_irq_callback_handler(struct dpu_kms *dpu_kms, int irq_idx)
153 {
154 	struct dpu_irq_callback *cb;
155 
156 	VERB("irq_idx=%d\n", irq_idx);
157 
158 	if (list_empty(&dpu_kms->hw_intr->irq_cb_tbl[irq_idx]))
159 		DRM_ERROR("no registered cb, idx:%d\n", irq_idx);
160 
161 	atomic_inc(&dpu_kms->hw_intr->irq_counts[irq_idx]);
162 
163 	/*
164 	 * Perform registered function callback
165 	 */
166 	list_for_each_entry(cb, &dpu_kms->hw_intr->irq_cb_tbl[irq_idx], list)
167 		if (cb->func)
168 			cb->func(cb->arg, irq_idx);
169 }
170 
171 irqreturn_t dpu_core_irq(struct dpu_kms *dpu_kms)
172 {
173 	struct dpu_hw_intr *intr = dpu_kms->hw_intr;
174 	int reg_idx;
175 	int irq_idx;
176 	u32 irq_status;
177 	u32 enable_mask;
178 	int bit;
179 	unsigned long irq_flags;
180 
181 	if (!intr)
182 		return IRQ_NONE;
183 
184 	spin_lock_irqsave(&intr->irq_lock, irq_flags);
185 	for (reg_idx = 0; reg_idx < ARRAY_SIZE(dpu_intr_set); reg_idx++) {
186 		if (!test_bit(reg_idx, &intr->irq_mask))
187 			continue;
188 
189 		/* Read interrupt status */
190 		irq_status = DPU_REG_READ(&intr->hw, dpu_intr_set[reg_idx].status_off);
191 
192 		/* Read enable mask */
193 		enable_mask = DPU_REG_READ(&intr->hw, dpu_intr_set[reg_idx].en_off);
194 
195 		/* and clear the interrupt */
196 		if (irq_status)
197 			DPU_REG_WRITE(&intr->hw, dpu_intr_set[reg_idx].clr_off,
198 				     irq_status);
199 
200 		/* Finally update IRQ status based on enable mask */
201 		irq_status &= enable_mask;
202 
203 		if (!irq_status)
204 			continue;
205 
206 		/*
207 		 * Search through matching intr status.
208 		 */
209 		while ((bit = ffs(irq_status)) != 0) {
210 			irq_idx = DPU_IRQ_IDX(reg_idx, bit - 1);
211 
212 			dpu_core_irq_callback_handler(dpu_kms, irq_idx);
213 
214 			/*
215 			 * When callback finish, clear the irq_status
216 			 * with the matching mask. Once irq_status
217 			 * is all cleared, the search can be stopped.
218 			 */
219 			irq_status &= ~BIT(bit - 1);
220 		}
221 	}
222 
223 	/* ensure register writes go through */
224 	wmb();
225 
226 	spin_unlock_irqrestore(&intr->irq_lock, irq_flags);
227 
228 	return IRQ_HANDLED;
229 }
230 
231 static int dpu_hw_intr_enable_irq_locked(struct dpu_hw_intr *intr, int irq_idx)
232 {
233 	int reg_idx;
234 	const struct dpu_intr_reg *reg;
235 	const char *dbgstr = NULL;
236 	uint32_t cache_irq_mask;
237 
238 	if (!intr)
239 		return -EINVAL;
240 
241 	if (irq_idx < 0 || irq_idx >= intr->total_irqs) {
242 		pr_err("invalid IRQ index: [%d]\n", irq_idx);
243 		return -EINVAL;
244 	}
245 
246 	/*
247 	 * The cache_irq_mask and hardware RMW operations needs to be done
248 	 * under irq_lock and it's the caller's responsibility to ensure that's
249 	 * held.
250 	 */
251 	assert_spin_locked(&intr->irq_lock);
252 
253 	reg_idx = DPU_IRQ_REG(irq_idx);
254 	reg = &dpu_intr_set[reg_idx];
255 
256 	cache_irq_mask = intr->cache_irq_mask[reg_idx];
257 	if (cache_irq_mask & DPU_IRQ_MASK(irq_idx)) {
258 		dbgstr = "DPU IRQ already set:";
259 	} else {
260 		dbgstr = "DPU IRQ enabled:";
261 
262 		cache_irq_mask |= DPU_IRQ_MASK(irq_idx);
263 		/* Cleaning any pending interrupt */
264 		DPU_REG_WRITE(&intr->hw, reg->clr_off, DPU_IRQ_MASK(irq_idx));
265 		/* Enabling interrupts with the new mask */
266 		DPU_REG_WRITE(&intr->hw, reg->en_off, cache_irq_mask);
267 
268 		/* ensure register write goes through */
269 		wmb();
270 
271 		intr->cache_irq_mask[reg_idx] = cache_irq_mask;
272 	}
273 
274 	pr_debug("%s MASK:0x%.8lx, CACHE-MASK:0x%.8x\n", dbgstr,
275 			DPU_IRQ_MASK(irq_idx), cache_irq_mask);
276 
277 	return 0;
278 }
279 
280 static int dpu_hw_intr_disable_irq_locked(struct dpu_hw_intr *intr, int irq_idx)
281 {
282 	int reg_idx;
283 	const struct dpu_intr_reg *reg;
284 	const char *dbgstr = NULL;
285 	uint32_t cache_irq_mask;
286 
287 	if (!intr)
288 		return -EINVAL;
289 
290 	if (irq_idx < 0 || irq_idx >= intr->total_irqs) {
291 		pr_err("invalid IRQ index: [%d]\n", irq_idx);
292 		return -EINVAL;
293 	}
294 
295 	/*
296 	 * The cache_irq_mask and hardware RMW operations needs to be done
297 	 * under irq_lock and it's the caller's responsibility to ensure that's
298 	 * held.
299 	 */
300 	assert_spin_locked(&intr->irq_lock);
301 
302 	reg_idx = DPU_IRQ_REG(irq_idx);
303 	reg = &dpu_intr_set[reg_idx];
304 
305 	cache_irq_mask = intr->cache_irq_mask[reg_idx];
306 	if ((cache_irq_mask & DPU_IRQ_MASK(irq_idx)) == 0) {
307 		dbgstr = "DPU IRQ is already cleared:";
308 	} else {
309 		dbgstr = "DPU IRQ mask disable:";
310 
311 		cache_irq_mask &= ~DPU_IRQ_MASK(irq_idx);
312 		/* Disable interrupts based on the new mask */
313 		DPU_REG_WRITE(&intr->hw, reg->en_off, cache_irq_mask);
314 		/* Cleaning any pending interrupt */
315 		DPU_REG_WRITE(&intr->hw, reg->clr_off, DPU_IRQ_MASK(irq_idx));
316 
317 		/* ensure register write goes through */
318 		wmb();
319 
320 		intr->cache_irq_mask[reg_idx] = cache_irq_mask;
321 	}
322 
323 	pr_debug("%s MASK:0x%.8lx, CACHE-MASK:0x%.8x\n", dbgstr,
324 			DPU_IRQ_MASK(irq_idx), cache_irq_mask);
325 
326 	return 0;
327 }
328 
329 static void dpu_clear_irqs(struct dpu_kms *dpu_kms)
330 {
331 	struct dpu_hw_intr *intr = dpu_kms->hw_intr;
332 	int i;
333 
334 	if (!intr)
335 		return;
336 
337 	for (i = 0; i < ARRAY_SIZE(dpu_intr_set); i++) {
338 		if (test_bit(i, &intr->irq_mask))
339 			DPU_REG_WRITE(&intr->hw,
340 					dpu_intr_set[i].clr_off, 0xffffffff);
341 	}
342 
343 	/* ensure register writes go through */
344 	wmb();
345 }
346 
347 static void dpu_disable_all_irqs(struct dpu_kms *dpu_kms)
348 {
349 	struct dpu_hw_intr *intr = dpu_kms->hw_intr;
350 	int i;
351 
352 	if (!intr)
353 		return;
354 
355 	for (i = 0; i < ARRAY_SIZE(dpu_intr_set); i++) {
356 		if (test_bit(i, &intr->irq_mask))
357 			DPU_REG_WRITE(&intr->hw,
358 					dpu_intr_set[i].en_off, 0x00000000);
359 	}
360 
361 	/* ensure register writes go through */
362 	wmb();
363 }
364 
365 u32 dpu_core_irq_read(struct dpu_kms *dpu_kms, int irq_idx, bool clear)
366 {
367 	struct dpu_hw_intr *intr = dpu_kms->hw_intr;
368 	int reg_idx;
369 	unsigned long irq_flags;
370 	u32 intr_status;
371 
372 	if (!intr)
373 		return 0;
374 
375 	if (irq_idx < 0) {
376 		DPU_ERROR("[%pS] invalid irq_idx=%d\n",
377 				__builtin_return_address(0), irq_idx);
378 		return 0;
379 	}
380 
381 	if (irq_idx < 0 || irq_idx >= intr->total_irqs) {
382 		pr_err("invalid IRQ index: [%d]\n", irq_idx);
383 		return 0;
384 	}
385 
386 	spin_lock_irqsave(&intr->irq_lock, irq_flags);
387 
388 	reg_idx = DPU_IRQ_REG(irq_idx);
389 	intr_status = DPU_REG_READ(&intr->hw,
390 			dpu_intr_set[reg_idx].status_off) &
391 		DPU_IRQ_MASK(irq_idx);
392 	if (intr_status && clear)
393 		DPU_REG_WRITE(&intr->hw, dpu_intr_set[reg_idx].clr_off,
394 				intr_status);
395 
396 	/* ensure register writes go through */
397 	wmb();
398 
399 	spin_unlock_irqrestore(&intr->irq_lock, irq_flags);
400 
401 	return intr_status;
402 }
403 
404 static void __intr_offset(struct dpu_mdss_cfg *m,
405 		void __iomem *addr, struct dpu_hw_blk_reg_map *hw)
406 {
407 	hw->base_off = addr;
408 	hw->blk_off = m->mdp[0].base;
409 	hw->hwversion = m->hwversion;
410 }
411 
412 struct dpu_hw_intr *dpu_hw_intr_init(void __iomem *addr,
413 		struct dpu_mdss_cfg *m)
414 {
415 	struct dpu_hw_intr *intr;
416 
417 	if (!addr || !m)
418 		return ERR_PTR(-EINVAL);
419 
420 	intr = kzalloc(sizeof(*intr), GFP_KERNEL);
421 	if (!intr)
422 		return ERR_PTR(-ENOMEM);
423 
424 	__intr_offset(m, addr, &intr->hw);
425 
426 	intr->total_irqs = ARRAY_SIZE(dpu_intr_set) * 32;
427 
428 	intr->cache_irq_mask = kcalloc(ARRAY_SIZE(dpu_intr_set), sizeof(u32),
429 			GFP_KERNEL);
430 	if (intr->cache_irq_mask == NULL) {
431 		kfree(intr);
432 		return ERR_PTR(-ENOMEM);
433 	}
434 
435 	intr->irq_mask = m->mdss_irqs;
436 
437 	spin_lock_init(&intr->irq_lock);
438 
439 	return intr;
440 }
441 
442 void dpu_hw_intr_destroy(struct dpu_hw_intr *intr)
443 {
444 	if (intr) {
445 		kfree(intr->cache_irq_mask);
446 
447 		kfree(intr->irq_cb_tbl);
448 		kfree(intr->irq_counts);
449 
450 		kfree(intr);
451 	}
452 }
453 
454 int dpu_core_irq_register_callback(struct dpu_kms *dpu_kms, int irq_idx,
455 		struct dpu_irq_callback *register_irq_cb)
456 {
457 	unsigned long irq_flags;
458 
459 	if (!dpu_kms->hw_intr->irq_cb_tbl) {
460 		DPU_ERROR("invalid params\n");
461 		return -EINVAL;
462 	}
463 
464 	if (!register_irq_cb || !register_irq_cb->func) {
465 		DPU_ERROR("invalid irq_cb:%d func:%d\n",
466 				register_irq_cb != NULL,
467 				register_irq_cb ?
468 					register_irq_cb->func != NULL : -1);
469 		return -EINVAL;
470 	}
471 
472 	if (irq_idx < 0 || irq_idx >= dpu_kms->hw_intr->total_irqs) {
473 		DPU_ERROR("invalid IRQ index: [%d]\n", irq_idx);
474 		return -EINVAL;
475 	}
476 
477 	VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
478 
479 	spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
480 	trace_dpu_core_irq_register_callback(irq_idx, register_irq_cb);
481 	list_del_init(&register_irq_cb->list);
482 	list_add_tail(&register_irq_cb->list,
483 			&dpu_kms->hw_intr->irq_cb_tbl[irq_idx]);
484 	if (list_is_first(&register_irq_cb->list,
485 			&dpu_kms->hw_intr->irq_cb_tbl[irq_idx])) {
486 		int ret = dpu_hw_intr_enable_irq_locked(
487 				dpu_kms->hw_intr,
488 				irq_idx);
489 		if (ret)
490 			DPU_ERROR("Fail to enable IRQ for irq_idx:%d\n",
491 					irq_idx);
492 	}
493 	spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
494 
495 	return 0;
496 }
497 
498 int dpu_core_irq_unregister_callback(struct dpu_kms *dpu_kms, int irq_idx,
499 		struct dpu_irq_callback *register_irq_cb)
500 {
501 	unsigned long irq_flags;
502 
503 	if (!dpu_kms->hw_intr->irq_cb_tbl) {
504 		DPU_ERROR("invalid params\n");
505 		return -EINVAL;
506 	}
507 
508 	if (!register_irq_cb || !register_irq_cb->func) {
509 		DPU_ERROR("invalid irq_cb:%d func:%d\n",
510 				register_irq_cb != NULL,
511 				register_irq_cb ?
512 					register_irq_cb->func != NULL : -1);
513 		return -EINVAL;
514 	}
515 
516 	if (irq_idx < 0 || irq_idx >= dpu_kms->hw_intr->total_irqs) {
517 		DPU_ERROR("invalid IRQ index: [%d]\n", irq_idx);
518 		return -EINVAL;
519 	}
520 
521 	VERB("[%pS] irq_idx=%d\n", __builtin_return_address(0), irq_idx);
522 
523 	spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
524 	trace_dpu_core_irq_unregister_callback(irq_idx, register_irq_cb);
525 	list_del_init(&register_irq_cb->list);
526 	/* empty callback list but interrupt is still enabled */
527 	if (list_empty(&dpu_kms->hw_intr->irq_cb_tbl[irq_idx])) {
528 		int ret = dpu_hw_intr_disable_irq_locked(
529 				dpu_kms->hw_intr,
530 				irq_idx);
531 		if (ret)
532 			DPU_ERROR("Fail to disable IRQ for irq_idx:%d\n",
533 					irq_idx);
534 		VERB("irq_idx=%d ret=%d\n", irq_idx, ret);
535 	}
536 	spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
537 
538 	return 0;
539 }
540 
541 #ifdef CONFIG_DEBUG_FS
542 static int dpu_debugfs_core_irq_show(struct seq_file *s, void *v)
543 {
544 	struct dpu_kms *dpu_kms = s->private;
545 	struct dpu_irq_callback *cb;
546 	unsigned long irq_flags;
547 	int i, irq_count, cb_count;
548 
549 	if (WARN_ON(!dpu_kms->hw_intr->irq_cb_tbl))
550 		return 0;
551 
552 	for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++) {
553 		spin_lock_irqsave(&dpu_kms->hw_intr->irq_lock, irq_flags);
554 		cb_count = 0;
555 		irq_count = atomic_read(&dpu_kms->hw_intr->irq_counts[i]);
556 		list_for_each_entry(cb, &dpu_kms->hw_intr->irq_cb_tbl[i], list)
557 			cb_count++;
558 		spin_unlock_irqrestore(&dpu_kms->hw_intr->irq_lock, irq_flags);
559 
560 		if (irq_count || cb_count)
561 			seq_printf(s, "idx:%d irq:%d cb:%d\n",
562 					i, irq_count, cb_count);
563 	}
564 
565 	return 0;
566 }
567 
568 DEFINE_SHOW_ATTRIBUTE(dpu_debugfs_core_irq);
569 
570 void dpu_debugfs_core_irq_init(struct dpu_kms *dpu_kms,
571 		struct dentry *parent)
572 {
573 	debugfs_create_file("core_irq", 0600, parent, dpu_kms,
574 		&dpu_debugfs_core_irq_fops);
575 }
576 #endif
577 
578 void dpu_core_irq_preinstall(struct dpu_kms *dpu_kms)
579 {
580 	int i;
581 
582 	pm_runtime_get_sync(&dpu_kms->pdev->dev);
583 	dpu_clear_irqs(dpu_kms);
584 	dpu_disable_all_irqs(dpu_kms);
585 	pm_runtime_put_sync(&dpu_kms->pdev->dev);
586 
587 	/* Create irq callbacks for all possible irq_idx */
588 	dpu_kms->hw_intr->irq_cb_tbl = kcalloc(dpu_kms->hw_intr->total_irqs,
589 			sizeof(struct list_head), GFP_KERNEL);
590 	dpu_kms->hw_intr->irq_counts = kcalloc(dpu_kms->hw_intr->total_irqs,
591 			sizeof(atomic_t), GFP_KERNEL);
592 	for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++) {
593 		INIT_LIST_HEAD(&dpu_kms->hw_intr->irq_cb_tbl[i]);
594 		atomic_set(&dpu_kms->hw_intr->irq_counts[i], 0);
595 	}
596 }
597 
598 void dpu_core_irq_uninstall(struct dpu_kms *dpu_kms)
599 {
600 	int i;
601 
602 	pm_runtime_get_sync(&dpu_kms->pdev->dev);
603 	for (i = 0; i < dpu_kms->hw_intr->total_irqs; i++)
604 		if (!list_empty(&dpu_kms->hw_intr->irq_cb_tbl[i]))
605 			DPU_ERROR("irq_idx=%d still enabled/registered\n", i);
606 
607 	dpu_clear_irqs(dpu_kms);
608 	dpu_disable_all_irqs(dpu_kms);
609 	pm_runtime_put_sync(&dpu_kms->pdev->dev);
610 }
611