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/clk.h>
  7#include <linux/delay.h>
  8#include <linux/interrupt.h>
  9#include <linux/of.h>
 10#include <linux/platform_device.h>
 11#include <linux/slab.h>
 12
 13#include "dcss-dev.h"
 14
 15#define DCSS_DTG_TC_CONTROL_STATUS			0x00
 16#define   CH3_EN					BIT(0)
 17#define   CH2_EN					BIT(1)
 18#define   CH1_EN					BIT(2)
 19#define   OVL_DATA_MODE					BIT(3)
 20#define   BLENDER_VIDEO_ALPHA_SEL			BIT(7)
 21#define   DTG_START					BIT(8)
 22#define   DBY_MODE_EN					BIT(9)
 23#define   CH1_ALPHA_SEL					BIT(10)
 24#define   CSS_PIX_COMP_SWAP_POS				12
 25#define   CSS_PIX_COMP_SWAP_MASK			GENMASK(14, 12)
 26#define   DEFAULT_FG_ALPHA_POS				24
 27#define   DEFAULT_FG_ALPHA_MASK				GENMASK(31, 24)
 28#define DCSS_DTG_TC_DTG					0x04
 29#define DCSS_DTG_TC_DISP_TOP				0x08
 30#define DCSS_DTG_TC_DISP_BOT				0x0C
 31#define DCSS_DTG_TC_CH1_TOP				0x10
 32#define DCSS_DTG_TC_CH1_BOT				0x14
 33#define DCSS_DTG_TC_CH2_TOP				0x18
 34#define DCSS_DTG_TC_CH2_BOT				0x1C
 35#define DCSS_DTG_TC_CH3_TOP				0x20
 36#define DCSS_DTG_TC_CH3_BOT				0x24
 37#define   TC_X_POS					0
 38#define   TC_X_MASK					GENMASK(12, 0)
 39#define   TC_Y_POS					16
 40#define   TC_Y_MASK					GENMASK(28, 16)
 41#define DCSS_DTG_TC_CTXLD				0x28
 42#define   TC_CTXLD_DB_Y_POS				0
 43#define   TC_CTXLD_DB_Y_MASK				GENMASK(12, 0)
 44#define   TC_CTXLD_SB_Y_POS				16
 45#define   TC_CTXLD_SB_Y_MASK				GENMASK(28, 16)
 46#define DCSS_DTG_TC_CH1_BKRND				0x2C
 47#define DCSS_DTG_TC_CH2_BKRND				0x30
 48#define   BKRND_R_Y_COMP_POS				20
 49#define   BKRND_R_Y_COMP_MASK				GENMASK(29, 20)
 50#define   BKRND_G_U_COMP_POS				10
 51#define   BKRND_G_U_COMP_MASK				GENMASK(19, 10)
 52#define   BKRND_B_V_COMP_POS				0
 53#define   BKRND_B_V_COMP_MASK				GENMASK(9, 0)
 54#define DCSS_DTG_BLENDER_DBY_RANGEINV			0x38
 55#define DCSS_DTG_BLENDER_DBY_RANGEMIN			0x3C
 56#define DCSS_DTG_BLENDER_DBY_BDP			0x40
 57#define DCSS_DTG_BLENDER_BKRND_I			0x44
 58#define DCSS_DTG_BLENDER_BKRND_P			0x48
 59#define DCSS_DTG_BLENDER_BKRND_T			0x4C
 60#define DCSS_DTG_LINE0_INT				0x50
 61#define DCSS_DTG_LINE1_INT				0x54
 62#define DCSS_DTG_BG_ALPHA_DEFAULT			0x58
 63#define DCSS_DTG_INT_STATUS				0x5C
 64#define DCSS_DTG_INT_CONTROL				0x60
 65#define DCSS_DTG_TC_CH3_BKRND				0x64
 66#define DCSS_DTG_INT_MASK				0x68
 67#define   LINE0_IRQ					BIT(0)
 68#define   LINE1_IRQ					BIT(1)
 69#define   LINE2_IRQ					BIT(2)
 70#define   LINE3_IRQ					BIT(3)
 71#define DCSS_DTG_LINE2_INT				0x6C
 72#define DCSS_DTG_LINE3_INT				0x70
 73#define DCSS_DTG_DBY_OL					0x74
 74#define DCSS_DTG_DBY_BL					0x78
 75#define DCSS_DTG_DBY_EL					0x7C
 76
 77struct dcss_dtg {
 78	struct device *dev;
 79	struct dcss_ctxld *ctxld;
 80	void __iomem *base_reg;
 81	u32 base_ofs;
 82
 83	u32 ctx_id;
 84
 85	bool in_use;
 86
 87	u32 dis_ulc_x;
 88	u32 dis_ulc_y;
 89
 90	u32 control_status;
 91	u32 alpha;
 92	u32 alpha_cfg;
 93
 94	int ctxld_kick_irq;
 95	bool ctxld_kick_irq_en;
 96};
 97
 98static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
 99{
100	if (!dtg->in_use)
101		dcss_writel(val, dtg->base_reg + ofs);
102
103	dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
104			 val, dtg->base_ofs + ofs);
105}
106
107static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
108{
109	struct dcss_dtg *dtg = data;
110	u32 status;
111
112	status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
113
114	if (!(status & LINE0_IRQ))
115		return IRQ_NONE;
116
117	dcss_ctxld_kick(dtg->ctxld);
118
119	dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
120
121	return IRQ_HANDLED;
122}
123
124static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
125			       struct platform_device *pdev)
126{
127	int ret;
128
129	dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
130	if (dtg->ctxld_kick_irq < 0)
131		return dtg->ctxld_kick_irq;
132
133	dcss_update(0, LINE0_IRQ | LINE1_IRQ,
134		    dtg->base_reg + DCSS_DTG_INT_MASK);
135
136	ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
137			  0, "dcss_ctxld_kick", dtg);
138	if (ret) {
139		dev_err(dtg->dev, "dtg: irq request failed.\n");
140		return ret;
141	}
142
143	disable_irq(dtg->ctxld_kick_irq);
144
145	dtg->ctxld_kick_irq_en = false;
146
147	return 0;
148}
149
150int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
151{
152	int ret = 0;
153	struct dcss_dtg *dtg;
154
155	dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
156	if (!dtg)
157		return -ENOMEM;
158
159	dcss->dtg = dtg;
160	dtg->dev = dcss->dev;
161	dtg->ctxld = dcss->ctxld;
162
163	dtg->base_reg = ioremap(dtg_base, SZ_4K);
164	if (!dtg->base_reg) {
165		dev_err(dcss->dev, "dtg: unable to remap dtg base\n");
166		ret = -ENOMEM;
167		goto err_ioremap;
168	}
169
170	dtg->base_ofs = dtg_base;
171	dtg->ctx_id = CTX_DB;
172
173	dtg->alpha = 255;
174
175	dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
176		((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
177
178	ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
179	if (ret)
180		goto err_irq;
181
182	return 0;
183
184err_irq:
185	iounmap(dtg->base_reg);
186
187err_ioremap:
188	kfree(dtg);
189
190	return ret;
191}
192
193void dcss_dtg_exit(struct dcss_dtg *dtg)
194{
195	free_irq(dtg->ctxld_kick_irq, dtg);
196
197	if (dtg->base_reg)
198		iounmap(dtg->base_reg);
199
200	kfree(dtg);
201}
202
203void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
204{
205	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
206	u16 dtg_lrc_x, dtg_lrc_y;
207	u16 dis_ulc_x, dis_ulc_y;
208	u16 dis_lrc_x, dis_lrc_y;
209	u32 sb_ctxld_trig, db_ctxld_trig;
210	u32 pixclock = vm->pixelclock;
211	u32 actual_clk;
212
213	dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
214		    vm->hactive - 1;
215	dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
216		    vm->vactive - 1;
217	dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
218	dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
219	dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
220	dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
221		    vm->vactive - 1;
222
223	clk_disable_unprepare(dcss->pix_clk);
224	clk_set_rate(dcss->pix_clk, vm->pixelclock);
225	clk_prepare_enable(dcss->pix_clk);
226
227	actual_clk = clk_get_rate(dcss->pix_clk);
228	if (pixclock != actual_clk) {
229		dev_info(dtg->dev,
230			 "Pixel clock set to %u kHz instead of %u kHz.\n",
231			 (actual_clk / 1000), (pixclock / 1000));
232	}
233
234	dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
235		       DCSS_DTG_TC_DTG);
236	dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
237		       DCSS_DTG_TC_DISP_TOP);
238	dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
239		       DCSS_DTG_TC_DISP_BOT);
240
241	dtg->dis_ulc_x = dis_ulc_x;
242	dtg->dis_ulc_y = dis_ulc_y;
243
244	sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
245							TC_CTXLD_SB_Y_MASK;
246	db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
247							TC_CTXLD_DB_Y_MASK;
248
249	dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
250
251	/* vblank trigger */
252	dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
253
254	/* CTXLD trigger */
255	dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
256}
257
258void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
259			    int px, int py, int pw, int ph)
260{
261	u16 p_ulc_x, p_ulc_y;
262	u16 p_lrc_x, p_lrc_y;
263
264	p_ulc_x = dtg->dis_ulc_x + px;
265	p_ulc_y = dtg->dis_ulc_y + py;
266	p_lrc_x = p_ulc_x + pw;
267	p_lrc_y = p_ulc_y + ph;
268
269	if (!px && !py && !pw && !ph) {
270		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
271		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
272	} else {
273		dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
274			       DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
275		dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
276			       DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
277	}
278}
279
280bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
281{
282	if (ch_num)
283		return false;
284
285	return alpha != dtg->alpha;
286}
287
288void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
289			      const struct drm_format_info *format, int alpha)
290{
291	/* we care about alpha only when channel 0 is concerned */
292	if (ch_num)
293		return;
294
295	/*
296	 * Use global alpha if pixel format does not have alpha channel or the
297	 * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
298	 */
299	if (!format->has_alpha || alpha != 255)
300		dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
301	else /* use per-pixel alpha otherwise */
302		dtg->alpha_cfg = CH1_ALPHA_SEL;
303
304	dtg->alpha = alpha;
305}
306
307void dcss_dtg_css_set(struct dcss_dtg *dtg)
308{
309	dtg->control_status |=
310			(0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
311}
312
313void dcss_dtg_enable(struct dcss_dtg *dtg)
314{
315	dtg->control_status |= DTG_START;
316
317	dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
318	dtg->control_status |= dtg->alpha_cfg;
319
320	dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
321
322	dtg->in_use = true;
323}
324
325void dcss_dtg_shutoff(struct dcss_dtg *dtg)
326{
327	dtg->control_status &= ~DTG_START;
328
329	dcss_writel(dtg->control_status,
330		    dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
331
332	dtg->in_use = false;
333}
334
335bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
336{
337	return dtg->in_use;
338}
339
340void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
341{
342	u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
343	u32 control_status;
344
345	control_status = dtg->control_status & ~ch_en_map[ch_num];
346	control_status |= en ? ch_en_map[ch_num] : 0;
347
348	control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
349	control_status |= dtg->alpha_cfg;
350
351	if (dtg->control_status != control_status)
352		dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
353
354	dtg->control_status = control_status;
355}
356
357void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
358{
359	u32 status;
360	u32 mask = en ? LINE1_IRQ : 0;
361
362	if (en) {
363		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
364		dcss_writel(status & LINE1_IRQ,
365			    dtg->base_reg + DCSS_DTG_INT_CONTROL);
366	}
367
368	dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
369}
370
371void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
372{
373	u32 status;
374	u32 mask = en ? LINE0_IRQ : 0;
375
376	if (en) {
377		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
378
379		if (!dtg->ctxld_kick_irq_en) {
380			dcss_writel(status & LINE0_IRQ,
381				    dtg->base_reg + DCSS_DTG_INT_CONTROL);
382			enable_irq(dtg->ctxld_kick_irq);
383			dtg->ctxld_kick_irq_en = true;
384			dcss_update(mask, LINE0_IRQ,
385				    dtg->base_reg + DCSS_DTG_INT_MASK);
386		}
387
388		return;
389	}
390
391	if (!dtg->ctxld_kick_irq_en)
392		return;
393
394	disable_irq_nosync(dtg->ctxld_kick_irq);
395	dtg->ctxld_kick_irq_en = false;
396
397	dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
398}
399
400void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
401{
402	dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
403}
404
405bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
406{
407	return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
408}
409