Linux 6.9-rc1
[linux-2.6-microblaze.git] / drivers / gpu / drm / imx / dcss / dcss-ctxld.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2019 NXP.
4  */
5
6 #include <linux/delay.h>
7 #include <linux/dma-mapping.h>
8 #include <linux/interrupt.h>
9 #include <linux/platform_device.h>
10 #include <linux/slab.h>
11
12 #include "dcss-dev.h"
13
14 #define DCSS_CTXLD_CONTROL_STATUS       0x0
15 #define   CTXLD_ENABLE                  BIT(0)
16 #define   ARB_SEL                       BIT(1)
17 #define   RD_ERR_EN                     BIT(2)
18 #define   DB_COMP_EN                    BIT(3)
19 #define   SB_HP_COMP_EN                 BIT(4)
20 #define   SB_LP_COMP_EN                 BIT(5)
21 #define   DB_PEND_SB_REC_EN             BIT(6)
22 #define   SB_PEND_DISP_ACTIVE_EN        BIT(7)
23 #define   AHB_ERR_EN                    BIT(8)
24 #define   RD_ERR                        BIT(16)
25 #define   DB_COMP                       BIT(17)
26 #define   SB_HP_COMP                    BIT(18)
27 #define   SB_LP_COMP                    BIT(19)
28 #define   DB_PEND_SB_REC                BIT(20)
29 #define   SB_PEND_DISP_ACTIVE           BIT(21)
30 #define   AHB_ERR                       BIT(22)
31 #define DCSS_CTXLD_DB_BASE_ADDR         0x10
32 #define DCSS_CTXLD_DB_COUNT             0x14
33 #define DCSS_CTXLD_SB_BASE_ADDR         0x18
34 #define DCSS_CTXLD_SB_COUNT             0x1C
35 #define   SB_HP_COUNT_POS               0
36 #define   SB_HP_COUNT_MASK              0xffff
37 #define   SB_LP_COUNT_POS               16
38 #define   SB_LP_COUNT_MASK              0xffff0000
39 #define DCSS_AHB_ERR_ADDR               0x20
40
41 #define CTXLD_IRQ_COMPLETION            (DB_COMP | SB_HP_COMP | SB_LP_COMP)
42 #define CTXLD_IRQ_ERROR                 (RD_ERR | DB_PEND_SB_REC | AHB_ERR)
43
44 /* The following sizes are in context loader entries, 8 bytes each. */
45 #define CTXLD_DB_CTX_ENTRIES            1024    /* max 65536 */
46 #define CTXLD_SB_LP_CTX_ENTRIES         10240   /* max 65536 */
47 #define CTXLD_SB_HP_CTX_ENTRIES         20000   /* max 65536 */
48 #define CTXLD_SB_CTX_ENTRIES            (CTXLD_SB_LP_CTX_ENTRIES + \
49                                          CTXLD_SB_HP_CTX_ENTRIES)
50
51 /* Sizes, in entries, of the DB, SB_HP and SB_LP context regions. */
52 static u16 dcss_ctxld_ctx_size[3] = {
53         CTXLD_DB_CTX_ENTRIES,
54         CTXLD_SB_HP_CTX_ENTRIES,
55         CTXLD_SB_LP_CTX_ENTRIES
56 };
57
58 /* this represents an entry in the context loader map */
59 struct dcss_ctxld_item {
60         u32 val;
61         u32 ofs;
62 };
63
64 #define CTX_ITEM_SIZE                   sizeof(struct dcss_ctxld_item)
65
66 struct dcss_ctxld {
67         struct device *dev;
68         void __iomem *ctxld_reg;
69         int irq;
70         bool irq_en;
71
72         struct dcss_ctxld_item *db[2];
73         struct dcss_ctxld_item *sb_hp[2];
74         struct dcss_ctxld_item *sb_lp[2];
75
76         dma_addr_t db_paddr[2];
77         dma_addr_t sb_paddr[2];
78
79         u16 ctx_size[2][3]; /* holds the sizes of DB, SB_HP and SB_LP ctx */
80         u8 current_ctx;
81
82         bool in_use;
83         bool armed;
84
85         spinlock_t lock; /* protects concurent access to private data */
86 };
87
88 static irqreturn_t dcss_ctxld_irq_handler(int irq, void *data)
89 {
90         struct dcss_ctxld *ctxld = data;
91         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
92         u32 irq_status;
93
94         irq_status = dcss_readl(ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
95
96         if (irq_status & CTXLD_IRQ_COMPLETION &&
97             !(irq_status & CTXLD_ENABLE) && ctxld->in_use) {
98                 ctxld->in_use = false;
99
100                 if (dcss && dcss->disable_callback)
101                         dcss->disable_callback(dcss);
102         } else if (irq_status & CTXLD_IRQ_ERROR) {
103                 /*
104                  * Except for throwing an error message and clearing the status
105                  * register, there's not much we can do here.
106                  */
107                 dev_err(ctxld->dev, "ctxld: error encountered: %08x\n",
108                         irq_status);
109                 dev_err(ctxld->dev, "ctxld: db=%d, sb_hp=%d, sb_lp=%d\n",
110                         ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_DB],
111                         ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_HP],
112                         ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_LP]);
113         }
114
115         dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION),
116                  ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
117
118         return IRQ_HANDLED;
119 }
120
121 static int dcss_ctxld_irq_config(struct dcss_ctxld *ctxld,
122                                  struct platform_device *pdev)
123 {
124         int ret;
125
126         ctxld->irq = platform_get_irq_byname(pdev, "ctxld");
127         if (ctxld->irq < 0)
128                 return ctxld->irq;
129
130         ret = request_irq(ctxld->irq, dcss_ctxld_irq_handler,
131                           0, "dcss_ctxld", ctxld);
132         if (ret) {
133                 dev_err(ctxld->dev, "ctxld: irq request failed.\n");
134                 return ret;
135         }
136
137         ctxld->irq_en = true;
138
139         return 0;
140 }
141
142 static void dcss_ctxld_hw_cfg(struct dcss_ctxld *ctxld)
143 {
144         dcss_writel(RD_ERR_EN | SB_HP_COMP_EN |
145                     DB_PEND_SB_REC_EN | AHB_ERR_EN | RD_ERR | AHB_ERR,
146                     ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
147 }
148
149 static void dcss_ctxld_free_ctx(struct dcss_ctxld *ctxld)
150 {
151         struct dcss_ctxld_item *ctx;
152         int i;
153
154         for (i = 0; i < 2; i++) {
155                 if (ctxld->db[i]) {
156                         dma_free_coherent(ctxld->dev,
157                                           CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
158                                           ctxld->db[i], ctxld->db_paddr[i]);
159                         ctxld->db[i] = NULL;
160                         ctxld->db_paddr[i] = 0;
161                 }
162
163                 if (ctxld->sb_hp[i]) {
164                         dma_free_coherent(ctxld->dev,
165                                           CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
166                                           ctxld->sb_hp[i], ctxld->sb_paddr[i]);
167                         ctxld->sb_hp[i] = NULL;
168                         ctxld->sb_paddr[i] = 0;
169                 }
170         }
171 }
172
173 static int dcss_ctxld_alloc_ctx(struct dcss_ctxld *ctxld)
174 {
175         struct dcss_ctxld_item *ctx;
176         int i;
177
178         for (i = 0; i < 2; i++) {
179                 ctx = dma_alloc_coherent(ctxld->dev,
180                                          CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
181                                          &ctxld->db_paddr[i], GFP_KERNEL);
182                 if (!ctx)
183                         return -ENOMEM;
184
185                 ctxld->db[i] = ctx;
186
187                 ctx = dma_alloc_coherent(ctxld->dev,
188                                          CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
189                                          &ctxld->sb_paddr[i], GFP_KERNEL);
190                 if (!ctx)
191                         return -ENOMEM;
192
193                 ctxld->sb_hp[i] = ctx;
194                 ctxld->sb_lp[i] = ctx + CTXLD_SB_HP_CTX_ENTRIES;
195         }
196
197         return 0;
198 }
199
200 int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base)
201 {
202         struct dcss_ctxld *ctxld;
203         int ret;
204
205         ctxld = devm_kzalloc(dcss->dev, sizeof(*ctxld), GFP_KERNEL);
206         if (!ctxld)
207                 return -ENOMEM;
208
209         dcss->ctxld = ctxld;
210         ctxld->dev = dcss->dev;
211
212         spin_lock_init(&ctxld->lock);
213
214         ret = dcss_ctxld_alloc_ctx(ctxld);
215         if (ret) {
216                 dev_err(dcss->dev, "ctxld: cannot allocate context memory.\n");
217                 goto err;
218         }
219
220         ctxld->ctxld_reg = devm_ioremap(dcss->dev, ctxld_base, SZ_4K);
221         if (!ctxld->ctxld_reg) {
222                 dev_err(dcss->dev, "ctxld: unable to remap ctxld base\n");
223                 ret = -ENOMEM;
224                 goto err;
225         }
226
227         ret = dcss_ctxld_irq_config(ctxld, to_platform_device(dcss->dev));
228         if (ret)
229                 goto err;
230
231         dcss_ctxld_hw_cfg(ctxld);
232
233         return 0;
234
235 err:
236         dcss_ctxld_free_ctx(ctxld);
237
238         return ret;
239 }
240
241 void dcss_ctxld_exit(struct dcss_ctxld *ctxld)
242 {
243         free_irq(ctxld->irq, ctxld);
244
245         dcss_ctxld_free_ctx(ctxld);
246 }
247
248 static int dcss_ctxld_enable_locked(struct dcss_ctxld *ctxld)
249 {
250         int curr_ctx = ctxld->current_ctx;
251         u32 db_base, sb_base, sb_count;
252         u32 sb_hp_cnt, sb_lp_cnt, db_cnt;
253         struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
254
255         if (!dcss)
256                 return 0;
257
258         dcss_dpr_write_sysctrl(dcss->dpr);
259
260         dcss_scaler_write_sclctrl(dcss->scaler);
261
262         sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP];
263         sb_lp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_LP];
264         db_cnt = ctxld->ctx_size[curr_ctx][CTX_DB];
265
266         /* make sure SB_LP context area comes after SB_HP */
267         if (sb_lp_cnt &&
268             ctxld->sb_lp[curr_ctx] != ctxld->sb_hp[curr_ctx] + sb_hp_cnt) {
269                 struct dcss_ctxld_item *sb_lp_adjusted;
270
271                 sb_lp_adjusted = ctxld->sb_hp[curr_ctx] + sb_hp_cnt;
272
273                 memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx],
274                        sb_lp_cnt * CTX_ITEM_SIZE);
275         }
276
277         db_base = db_cnt ? ctxld->db_paddr[curr_ctx] : 0;
278
279         dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_ADDR);
280         dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT);
281
282         if (sb_hp_cnt)
283                 sb_count = ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK) |
284                            ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_COUNT_MASK);
285         else
286                 sb_count = (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK;
287
288         sb_base = sb_count ? ctxld->sb_paddr[curr_ctx] : 0;
289
290         dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_ADDR);
291         dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT);
292
293         /* enable the context loader */
294         dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
295
296         ctxld->in_use = true;
297
298         /*
299          * Toggle the current context to the alternate one so that any updates
300          * in the modules' settings take place there.
301          */
302         ctxld->current_ctx ^= 1;
303
304         ctxld->ctx_size[ctxld->current_ctx][CTX_DB] = 0;
305         ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] = 0;
306         ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] = 0;
307
308         return 0;
309 }
310
311 int dcss_ctxld_enable(struct dcss_ctxld *ctxld)
312 {
313         spin_lock_irq(&ctxld->lock);
314         ctxld->armed = true;
315         spin_unlock_irq(&ctxld->lock);
316
317         return 0;
318 }
319
320 void dcss_ctxld_kick(struct dcss_ctxld *ctxld)
321 {
322         unsigned long flags;
323
324         spin_lock_irqsave(&ctxld->lock, flags);
325         if (ctxld->armed && !ctxld->in_use) {
326                 ctxld->armed = false;
327                 dcss_ctxld_enable_locked(ctxld);
328         }
329         spin_unlock_irqrestore(&ctxld->lock, flags);
330 }
331
332 void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, u32 val,
333                               u32 reg_ofs)
334 {
335         int curr_ctx = ctxld->current_ctx;
336         struct dcss_ctxld_item *ctx[] = {
337                 [CTX_DB] = ctxld->db[curr_ctx],
338                 [CTX_SB_HP] = ctxld->sb_hp[curr_ctx],
339                 [CTX_SB_LP] = ctxld->sb_lp[curr_ctx]
340         };
341         int item_idx = ctxld->ctx_size[curr_ctx][ctx_id];
342
343         if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) {
344                 WARN_ON(1);
345                 return;
346         }
347
348         ctx[ctx_id][item_idx].val = val;
349         ctx[ctx_id][item_idx].ofs = reg_ofs;
350         ctxld->ctx_size[curr_ctx][ctx_id] += 1;
351 }
352
353 void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id,
354                       u32 val, u32 reg_ofs)
355 {
356         spin_lock_irq(&ctxld->lock);
357         dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs);
358         spin_unlock_irq(&ctxld->lock);
359 }
360
361 bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld)
362 {
363         return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 &&
364                 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 &&
365                 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0;
366 }
367
368 int dcss_ctxld_resume(struct dcss_ctxld *ctxld)
369 {
370         dcss_ctxld_hw_cfg(ctxld);
371
372         if (!ctxld->irq_en) {
373                 enable_irq(ctxld->irq);
374                 ctxld->irq_en = true;
375         }
376
377         return 0;
378 }
379
380 int dcss_ctxld_suspend(struct dcss_ctxld *ctxld)
381 {
382         int ret = 0;
383         unsigned long timeout = jiffies + msecs_to_jiffies(500);
384
385         if (!dcss_ctxld_is_flushed(ctxld)) {
386                 dcss_ctxld_kick(ctxld);
387
388                 while (!time_after(jiffies, timeout) && ctxld->in_use)
389                         msleep(20);
390
391                 if (time_after(jiffies, timeout))
392                         return -ETIMEDOUT;
393         }
394
395         spin_lock_irq(&ctxld->lock);
396
397         if (ctxld->irq_en) {
398                 disable_irq_nosync(ctxld->irq);
399                 ctxld->irq_en = false;
400         }
401
402         /* reset context region and sizes */
403         ctxld->current_ctx = 0;
404         ctxld->ctx_size[0][CTX_DB] = 0;
405         ctxld->ctx_size[0][CTX_SB_HP] = 0;
406         ctxld->ctx_size[0][CTX_SB_LP] = 0;
407
408         spin_unlock_irq(&ctxld->lock);
409
410         return ret;
411 }
412
413 void dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld)
414 {
415         lockdep_assert_held(&ctxld->lock);
416 }