brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 2968f5d Raw
394 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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			0x0016#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				1225#define   CSS_PIX_COMP_SWAP_MASK			GENMASK(14, 12)26#define   DEFAULT_FG_ALPHA_POS				2427#define   DEFAULT_FG_ALPHA_MASK				GENMASK(31, 24)28#define DCSS_DTG_TC_DTG					0x0429#define DCSS_DTG_TC_DISP_TOP				0x0830#define DCSS_DTG_TC_DISP_BOT				0x0C31#define DCSS_DTG_TC_CH1_TOP				0x1032#define DCSS_DTG_TC_CH1_BOT				0x1433#define DCSS_DTG_TC_CH2_TOP				0x1834#define DCSS_DTG_TC_CH2_BOT				0x1C35#define DCSS_DTG_TC_CH3_TOP				0x2036#define DCSS_DTG_TC_CH3_BOT				0x2437#define   TC_X_POS					038#define   TC_X_MASK					GENMASK(12, 0)39#define   TC_Y_POS					1640#define   TC_Y_MASK					GENMASK(28, 16)41#define DCSS_DTG_TC_CTXLD				0x2842#define   TC_CTXLD_DB_Y_POS				043#define   TC_CTXLD_DB_Y_MASK				GENMASK(12, 0)44#define   TC_CTXLD_SB_Y_POS				1645#define   TC_CTXLD_SB_Y_MASK				GENMASK(28, 16)46#define DCSS_DTG_TC_CH1_BKRND				0x2C47#define DCSS_DTG_TC_CH2_BKRND				0x3048#define   BKRND_R_Y_COMP_POS				2049#define   BKRND_R_Y_COMP_MASK				GENMASK(29, 20)50#define   BKRND_G_U_COMP_POS				1051#define   BKRND_G_U_COMP_MASK				GENMASK(19, 10)52#define   BKRND_B_V_COMP_POS				053#define   BKRND_B_V_COMP_MASK				GENMASK(9, 0)54#define DCSS_DTG_BLENDER_DBY_RANGEINV			0x3855#define DCSS_DTG_BLENDER_DBY_RANGEMIN			0x3C56#define DCSS_DTG_BLENDER_DBY_BDP			0x4057#define DCSS_DTG_BLENDER_BKRND_I			0x4458#define DCSS_DTG_BLENDER_BKRND_P			0x4859#define DCSS_DTG_BLENDER_BKRND_T			0x4C60#define DCSS_DTG_LINE0_INT				0x5061#define DCSS_DTG_LINE1_INT				0x5462#define DCSS_DTG_BG_ALPHA_DEFAULT			0x5863#define DCSS_DTG_INT_STATUS				0x5C64#define DCSS_DTG_INT_CONTROL				0x6065#define DCSS_DTG_TC_CH3_BKRND				0x6466#define DCSS_DTG_INT_MASK				0x6867#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				0x6C72#define DCSS_DTG_LINE3_INT				0x7073#define DCSS_DTG_DBY_OL					0x7474#define DCSS_DTG_DBY_BL					0x7875#define DCSS_DTG_DBY_EL					0x7C76 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 = devm_kzalloc(dcss->dev, 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 = devm_ioremap(dtg->dev, dtg_base, SZ_4K);164	if (!dtg->base_reg) {165		dev_err(dtg->dev, "dtg: unable to remap dtg base\n");166		return -ENOMEM;167	}168 169	dtg->base_ofs = dtg_base;170	dtg->ctx_id = CTX_DB;171 172	dtg->alpha = 255;173 174	dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |175		((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);176 177	ret = dcss_dtg_irq_config(dtg, to_platform_device(dtg->dev));178 179	return ret;180}181 182void dcss_dtg_exit(struct dcss_dtg *dtg)183{184	free_irq(dtg->ctxld_kick_irq, dtg);185}186 187void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)188{189	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);190	u16 dtg_lrc_x, dtg_lrc_y;191	u16 dis_ulc_x, dis_ulc_y;192	u16 dis_lrc_x, dis_lrc_y;193	u32 sb_ctxld_trig, db_ctxld_trig;194	u32 pixclock = vm->pixelclock;195	u32 actual_clk;196 197	dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +198		    vm->hactive - 1;199	dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +200		    vm->vactive - 1;201	dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;202	dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;203	dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;204	dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +205		    vm->vactive - 1;206 207	clk_disable_unprepare(dcss->pix_clk);208	clk_set_rate(dcss->pix_clk, vm->pixelclock);209	clk_prepare_enable(dcss->pix_clk);210 211	actual_clk = clk_get_rate(dcss->pix_clk);212	if (pixclock != actual_clk) {213		dev_info(dtg->dev,214			 "Pixel clock set to %u kHz instead of %u kHz.\n",215			 (actual_clk / 1000), (pixclock / 1000));216	}217 218	dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),219		       DCSS_DTG_TC_DTG);220	dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),221		       DCSS_DTG_TC_DISP_TOP);222	dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),223		       DCSS_DTG_TC_DISP_BOT);224 225	dtg->dis_ulc_x = dis_ulc_x;226	dtg->dis_ulc_y = dis_ulc_y;227 228	sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &229							TC_CTXLD_SB_Y_MASK;230	db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &231							TC_CTXLD_DB_Y_MASK;232 233	dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);234 235	/* vblank trigger */236	dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);237 238	/* CTXLD trigger */239	dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);240}241 242void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,243			    int px, int py, int pw, int ph)244{245	u16 p_ulc_x, p_ulc_y;246	u16 p_lrc_x, p_lrc_y;247 248	p_ulc_x = dtg->dis_ulc_x + px;249	p_ulc_y = dtg->dis_ulc_y + py;250	p_lrc_x = p_ulc_x + pw;251	p_lrc_y = p_ulc_y + ph;252 253	if (!px && !py && !pw && !ph) {254		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);255		dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);256	} else {257		dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),258			       DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);259		dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),260			       DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);261	}262}263 264bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)265{266	if (ch_num)267		return false;268 269	return alpha != dtg->alpha;270}271 272void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,273			      const struct drm_format_info *format, int alpha)274{275	/* we care about alpha only when channel 0 is concerned */276	if (ch_num)277		return;278 279	/*280	 * Use global alpha if pixel format does not have alpha channel or the281	 * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).282	 */283	if (!format->has_alpha || alpha != 255)284		dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;285	else /* use per-pixel alpha otherwise */286		dtg->alpha_cfg = CH1_ALPHA_SEL;287 288	dtg->alpha = alpha;289}290 291void dcss_dtg_css_set(struct dcss_dtg *dtg)292{293	dtg->control_status |=294			(0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;295}296 297void dcss_dtg_enable(struct dcss_dtg *dtg)298{299	dtg->control_status |= DTG_START;300 301	dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);302	dtg->control_status |= dtg->alpha_cfg;303 304	dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);305 306	dtg->in_use = true;307}308 309void dcss_dtg_shutoff(struct dcss_dtg *dtg)310{311	dtg->control_status &= ~DTG_START;312 313	dcss_writel(dtg->control_status,314		    dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);315 316	dtg->in_use = false;317}318 319bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)320{321	return dtg->in_use;322}323 324void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)325{326	u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};327	u32 control_status;328 329	control_status = dtg->control_status & ~ch_en_map[ch_num];330	control_status |= en ? ch_en_map[ch_num] : 0;331 332	control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);333	control_status |= dtg->alpha_cfg;334 335	if (dtg->control_status != control_status)336		dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);337 338	dtg->control_status = control_status;339}340 341void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)342{343	u32 status;344	u32 mask = en ? LINE1_IRQ : 0;345 346	if (en) {347		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);348		dcss_writel(status & LINE1_IRQ,349			    dtg->base_reg + DCSS_DTG_INT_CONTROL);350	}351 352	dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);353}354 355void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)356{357	u32 status;358	u32 mask = en ? LINE0_IRQ : 0;359 360	if (en) {361		status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);362 363		if (!dtg->ctxld_kick_irq_en) {364			dcss_writel(status & LINE0_IRQ,365				    dtg->base_reg + DCSS_DTG_INT_CONTROL);366			enable_irq(dtg->ctxld_kick_irq);367			dtg->ctxld_kick_irq_en = true;368			dcss_update(mask, LINE0_IRQ,369				    dtg->base_reg + DCSS_DTG_INT_MASK);370		}371 372		return;373	}374 375	if (!dtg->ctxld_kick_irq_en)376		return;377 378	disable_irq_nosync(dtg->ctxld_kick_irq);379	dtg->ctxld_kick_irq_en = false;380 381	dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);382}383 384void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)385{386	dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);387}388 389bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)390{391	return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);392}393 394