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 = kzalloc(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 = ioremap(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_irq; 230 231 dcss_ctxld_hw_cfg(ctxld); 232 233 return 0; 234 235 err_irq: 236 iounmap(ctxld->ctxld_reg); 237 238 err: 239 dcss_ctxld_free_ctx(ctxld); 240 kfree(ctxld); 241 242 return ret; 243 } 244 245 void dcss_ctxld_exit(struct dcss_ctxld *ctxld) 246 { 247 free_irq(ctxld->irq, ctxld); 248 249 if (ctxld->ctxld_reg) 250 iounmap(ctxld->ctxld_reg); 251 252 dcss_ctxld_free_ctx(ctxld); 253 kfree(ctxld); 254 } 255 256 static int dcss_ctxld_enable_locked(struct dcss_ctxld *ctxld) 257 { 258 int curr_ctx = ctxld->current_ctx; 259 u32 db_base, sb_base, sb_count; 260 u32 sb_hp_cnt, sb_lp_cnt, db_cnt; 261 struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev); 262 263 if (!dcss) 264 return 0; 265 266 dcss_dpr_write_sysctrl(dcss->dpr); 267 268 dcss_scaler_write_sclctrl(dcss->scaler); 269 270 sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP]; 271 sb_lp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_LP]; 272 db_cnt = ctxld->ctx_size[curr_ctx][CTX_DB]; 273 274 /* make sure SB_LP context area comes after SB_HP */ 275 if (sb_lp_cnt && 276 ctxld->sb_lp[curr_ctx] != ctxld->sb_hp[curr_ctx] + sb_hp_cnt) { 277 struct dcss_ctxld_item *sb_lp_adjusted; 278 279 sb_lp_adjusted = ctxld->sb_hp[curr_ctx] + sb_hp_cnt; 280 281 memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx], 282 sb_lp_cnt * CTX_ITEM_SIZE); 283 } 284 285 db_base = db_cnt ? ctxld->db_paddr[curr_ctx] : 0; 286 287 dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_ADDR); 288 dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT); 289 290 if (sb_hp_cnt) 291 sb_count = ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK) | 292 ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_COUNT_MASK); 293 else 294 sb_count = (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK; 295 296 sb_base = sb_count ? ctxld->sb_paddr[curr_ctx] : 0; 297 298 dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_ADDR); 299 dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT); 300 301 /* enable the context loader */ 302 dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS); 303 304 ctxld->in_use = true; 305 306 /* 307 * Toggle the current context to the alternate one so that any updates 308 * in the modules' settings take place there. 309 */ 310 ctxld->current_ctx ^= 1; 311 312 ctxld->ctx_size[ctxld->current_ctx][CTX_DB] = 0; 313 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] = 0; 314 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] = 0; 315 316 return 0; 317 } 318 319 int dcss_ctxld_enable(struct dcss_ctxld *ctxld) 320 { 321 spin_lock_irq(&ctxld->lock); 322 ctxld->armed = true; 323 spin_unlock_irq(&ctxld->lock); 324 325 return 0; 326 } 327 328 void dcss_ctxld_kick(struct dcss_ctxld *ctxld) 329 { 330 unsigned long flags; 331 332 spin_lock_irqsave(&ctxld->lock, flags); 333 if (ctxld->armed && !ctxld->in_use) { 334 ctxld->armed = false; 335 dcss_ctxld_enable_locked(ctxld); 336 } 337 spin_unlock_irqrestore(&ctxld->lock, flags); 338 } 339 340 void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, u32 val, 341 u32 reg_ofs) 342 { 343 int curr_ctx = ctxld->current_ctx; 344 struct dcss_ctxld_item *ctx[] = { 345 [CTX_DB] = ctxld->db[curr_ctx], 346 [CTX_SB_HP] = ctxld->sb_hp[curr_ctx], 347 [CTX_SB_LP] = ctxld->sb_lp[curr_ctx] 348 }; 349 int item_idx = ctxld->ctx_size[curr_ctx][ctx_id]; 350 351 if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) { 352 WARN_ON(1); 353 return; 354 } 355 356 ctx[ctx_id][item_idx].val = val; 357 ctx[ctx_id][item_idx].ofs = reg_ofs; 358 ctxld->ctx_size[curr_ctx][ctx_id] += 1; 359 } 360 361 void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id, 362 u32 val, u32 reg_ofs) 363 { 364 spin_lock_irq(&ctxld->lock); 365 dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs); 366 spin_unlock_irq(&ctxld->lock); 367 } 368 369 bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld) 370 { 371 return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 && 372 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 && 373 ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0; 374 } 375 376 int dcss_ctxld_resume(struct dcss_ctxld *ctxld) 377 { 378 dcss_ctxld_hw_cfg(ctxld); 379 380 if (!ctxld->irq_en) { 381 enable_irq(ctxld->irq); 382 ctxld->irq_en = true; 383 } 384 385 return 0; 386 } 387 388 int dcss_ctxld_suspend(struct dcss_ctxld *ctxld) 389 { 390 int ret = 0; 391 unsigned long timeout = jiffies + msecs_to_jiffies(500); 392 393 if (!dcss_ctxld_is_flushed(ctxld)) { 394 dcss_ctxld_kick(ctxld); 395 396 while (!time_after(jiffies, timeout) && ctxld->in_use) 397 msleep(20); 398 399 if (time_after(jiffies, timeout)) 400 return -ETIMEDOUT; 401 } 402 403 spin_lock_irq(&ctxld->lock); 404 405 if (ctxld->irq_en) { 406 disable_irq_nosync(ctxld->irq); 407 ctxld->irq_en = false; 408 } 409 410 /* reset context region and sizes */ 411 ctxld->current_ctx = 0; 412 ctxld->ctx_size[0][CTX_DB] = 0; 413 ctxld->ctx_size[0][CTX_SB_HP] = 0; 414 ctxld->ctx_size[0][CTX_SB_LP] = 0; 415 416 spin_unlock_irq(&ctxld->lock); 417 418 return ret; 419 } 420 421 void dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld) 422 { 423 lockdep_assert_held(&ctxld->lock); 424 } 425