Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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}