dcss-ctxld.c (10228B)
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. */ 52static 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 */ 59struct dcss_ctxld_item { 60 u32 val; 61 u32 ofs; 62}; 63 64#define CTX_ITEM_SIZE sizeof(struct dcss_ctxld_item) 65 66struct 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 88static 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 121static 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 142static 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 149static 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 173static 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 200int 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 235err_irq: 236 iounmap(ctxld->ctxld_reg); 237 238err: 239 dcss_ctxld_free_ctx(ctxld); 240 kfree(ctxld); 241 242 return ret; 243} 244 245void 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 256static 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 319int 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 328void 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 340void 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 361void 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 369bool 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 376int 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 388int 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 421void dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld) 422{ 423 lockdep_assert_held(&ctxld->lock); 424}