123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright 2019 NXP.
- */
- #include <linux/clk.h>
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include "dcss-dev.h"
- #define DCSS_DTG_TC_CONTROL_STATUS 0x00
- #define CH3_EN BIT(0)
- #define CH2_EN BIT(1)
- #define CH1_EN BIT(2)
- #define OVL_DATA_MODE BIT(3)
- #define BLENDER_VIDEO_ALPHA_SEL BIT(7)
- #define DTG_START BIT(8)
- #define DBY_MODE_EN BIT(9)
- #define CH1_ALPHA_SEL BIT(10)
- #define CSS_PIX_COMP_SWAP_POS 12
- #define CSS_PIX_COMP_SWAP_MASK GENMASK(14, 12)
- #define DEFAULT_FG_ALPHA_POS 24
- #define DEFAULT_FG_ALPHA_MASK GENMASK(31, 24)
- #define DCSS_DTG_TC_DTG 0x04
- #define DCSS_DTG_TC_DISP_TOP 0x08
- #define DCSS_DTG_TC_DISP_BOT 0x0C
- #define DCSS_DTG_TC_CH1_TOP 0x10
- #define DCSS_DTG_TC_CH1_BOT 0x14
- #define DCSS_DTG_TC_CH2_TOP 0x18
- #define DCSS_DTG_TC_CH2_BOT 0x1C
- #define DCSS_DTG_TC_CH3_TOP 0x20
- #define DCSS_DTG_TC_CH3_BOT 0x24
- #define TC_X_POS 0
- #define TC_X_MASK GENMASK(12, 0)
- #define TC_Y_POS 16
- #define TC_Y_MASK GENMASK(28, 16)
- #define DCSS_DTG_TC_CTXLD 0x28
- #define TC_CTXLD_DB_Y_POS 0
- #define TC_CTXLD_DB_Y_MASK GENMASK(12, 0)
- #define TC_CTXLD_SB_Y_POS 16
- #define TC_CTXLD_SB_Y_MASK GENMASK(28, 16)
- #define DCSS_DTG_TC_CH1_BKRND 0x2C
- #define DCSS_DTG_TC_CH2_BKRND 0x30
- #define BKRND_R_Y_COMP_POS 20
- #define BKRND_R_Y_COMP_MASK GENMASK(29, 20)
- #define BKRND_G_U_COMP_POS 10
- #define BKRND_G_U_COMP_MASK GENMASK(19, 10)
- #define BKRND_B_V_COMP_POS 0
- #define BKRND_B_V_COMP_MASK GENMASK(9, 0)
- #define DCSS_DTG_BLENDER_DBY_RANGEINV 0x38
- #define DCSS_DTG_BLENDER_DBY_RANGEMIN 0x3C
- #define DCSS_DTG_BLENDER_DBY_BDP 0x40
- #define DCSS_DTG_BLENDER_BKRND_I 0x44
- #define DCSS_DTG_BLENDER_BKRND_P 0x48
- #define DCSS_DTG_BLENDER_BKRND_T 0x4C
- #define DCSS_DTG_LINE0_INT 0x50
- #define DCSS_DTG_LINE1_INT 0x54
- #define DCSS_DTG_BG_ALPHA_DEFAULT 0x58
- #define DCSS_DTG_INT_STATUS 0x5C
- #define DCSS_DTG_INT_CONTROL 0x60
- #define DCSS_DTG_TC_CH3_BKRND 0x64
- #define DCSS_DTG_INT_MASK 0x68
- #define LINE0_IRQ BIT(0)
- #define LINE1_IRQ BIT(1)
- #define LINE2_IRQ BIT(2)
- #define LINE3_IRQ BIT(3)
- #define DCSS_DTG_LINE2_INT 0x6C
- #define DCSS_DTG_LINE3_INT 0x70
- #define DCSS_DTG_DBY_OL 0x74
- #define DCSS_DTG_DBY_BL 0x78
- #define DCSS_DTG_DBY_EL 0x7C
- struct dcss_dtg {
- struct device *dev;
- struct dcss_ctxld *ctxld;
- void __iomem *base_reg;
- u32 base_ofs;
- u32 ctx_id;
- bool in_use;
- u32 dis_ulc_x;
- u32 dis_ulc_y;
- u32 control_status;
- u32 alpha;
- u32 alpha_cfg;
- int ctxld_kick_irq;
- bool ctxld_kick_irq_en;
- };
- static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
- {
- if (!dtg->in_use)
- dcss_writel(val, dtg->base_reg + ofs);
- dcss_ctxld_write(dtg->ctxld, dtg->ctx_id,
- val, dtg->base_ofs + ofs);
- }
- static irqreturn_t dcss_dtg_irq_handler(int irq, void *data)
- {
- struct dcss_dtg *dtg = data;
- u32 status;
- status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
- if (!(status & LINE0_IRQ))
- return IRQ_NONE;
- dcss_ctxld_kick(dtg->ctxld);
- dcss_writel(status & LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
- return IRQ_HANDLED;
- }
- static int dcss_dtg_irq_config(struct dcss_dtg *dtg,
- struct platform_device *pdev)
- {
- int ret;
- dtg->ctxld_kick_irq = platform_get_irq_byname(pdev, "ctxld_kick");
- if (dtg->ctxld_kick_irq < 0)
- return dtg->ctxld_kick_irq;
- dcss_update(0, LINE0_IRQ | LINE1_IRQ,
- dtg->base_reg + DCSS_DTG_INT_MASK);
- ret = request_irq(dtg->ctxld_kick_irq, dcss_dtg_irq_handler,
- 0, "dcss_ctxld_kick", dtg);
- if (ret) {
- dev_err(dtg->dev, "dtg: irq request failed.\n");
- return ret;
- }
- disable_irq(dtg->ctxld_kick_irq);
- dtg->ctxld_kick_irq_en = false;
- return 0;
- }
- int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
- {
- int ret = 0;
- struct dcss_dtg *dtg;
- dtg = kzalloc(sizeof(*dtg), GFP_KERNEL);
- if (!dtg)
- return -ENOMEM;
- dcss->dtg = dtg;
- dtg->dev = dcss->dev;
- dtg->ctxld = dcss->ctxld;
- dtg->base_reg = ioremap(dtg_base, SZ_4K);
- if (!dtg->base_reg) {
- dev_err(dcss->dev, "dtg: unable to remap dtg base\n");
- ret = -ENOMEM;
- goto err_ioremap;
- }
- dtg->base_ofs = dtg_base;
- dtg->ctx_id = CTX_DB;
- dtg->alpha = 255;
- dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
- ((dtg->alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK);
- ret = dcss_dtg_irq_config(dtg, to_platform_device(dcss->dev));
- if (ret)
- goto err_irq;
- return 0;
- err_irq:
- iounmap(dtg->base_reg);
- err_ioremap:
- kfree(dtg);
- return ret;
- }
- void dcss_dtg_exit(struct dcss_dtg *dtg)
- {
- free_irq(dtg->ctxld_kick_irq, dtg);
- if (dtg->base_reg)
- iounmap(dtg->base_reg);
- kfree(dtg);
- }
- void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
- {
- struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dtg->dev);
- u16 dtg_lrc_x, dtg_lrc_y;
- u16 dis_ulc_x, dis_ulc_y;
- u16 dis_lrc_x, dis_lrc_y;
- u32 sb_ctxld_trig, db_ctxld_trig;
- u32 pixclock = vm->pixelclock;
- u32 actual_clk;
- dtg_lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
- vm->hactive - 1;
- dtg_lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
- vm->vactive - 1;
- dis_ulc_x = vm->hsync_len + vm->hback_porch - 1;
- dis_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch - 1;
- dis_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
- dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
- vm->vactive - 1;
- clk_disable_unprepare(dcss->pix_clk);
- clk_set_rate(dcss->pix_clk, vm->pixelclock);
- clk_prepare_enable(dcss->pix_clk);
- actual_clk = clk_get_rate(dcss->pix_clk);
- if (pixclock != actual_clk) {
- dev_info(dtg->dev,
- "Pixel clock set to %u kHz instead of %u kHz.\n",
- (actual_clk / 1000), (pixclock / 1000));
- }
- dcss_dtg_write(dtg, ((dtg_lrc_y << TC_Y_POS) | dtg_lrc_x),
- DCSS_DTG_TC_DTG);
- dcss_dtg_write(dtg, ((dis_ulc_y << TC_Y_POS) | dis_ulc_x),
- DCSS_DTG_TC_DISP_TOP);
- dcss_dtg_write(dtg, ((dis_lrc_y << TC_Y_POS) | dis_lrc_x),
- DCSS_DTG_TC_DISP_BOT);
- dtg->dis_ulc_x = dis_ulc_x;
- dtg->dis_ulc_y = dis_ulc_y;
- sb_ctxld_trig = ((0 * dis_lrc_y / 100) << TC_CTXLD_SB_Y_POS) &
- TC_CTXLD_SB_Y_MASK;
- db_ctxld_trig = ((99 * dis_lrc_y / 100) << TC_CTXLD_DB_Y_POS) &
- TC_CTXLD_DB_Y_MASK;
- dcss_dtg_write(dtg, sb_ctxld_trig | db_ctxld_trig, DCSS_DTG_TC_CTXLD);
- /* vblank trigger */
- dcss_dtg_write(dtg, 0, DCSS_DTG_LINE1_INT);
- /* CTXLD trigger */
- dcss_dtg_write(dtg, ((90 * dis_lrc_y) / 100) << 16, DCSS_DTG_LINE0_INT);
- }
- void dcss_dtg_plane_pos_set(struct dcss_dtg *dtg, int ch_num,
- int px, int py, int pw, int ph)
- {
- u16 p_ulc_x, p_ulc_y;
- u16 p_lrc_x, p_lrc_y;
- p_ulc_x = dtg->dis_ulc_x + px;
- p_ulc_y = dtg->dis_ulc_y + py;
- p_lrc_x = p_ulc_x + pw;
- p_lrc_y = p_ulc_y + ph;
- if (!px && !py && !pw && !ph) {
- dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
- dcss_dtg_write(dtg, 0, DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
- } else {
- dcss_dtg_write(dtg, ((p_ulc_y << TC_Y_POS) | p_ulc_x),
- DCSS_DTG_TC_CH1_TOP + 0x8 * ch_num);
- dcss_dtg_write(dtg, ((p_lrc_y << TC_Y_POS) | p_lrc_x),
- DCSS_DTG_TC_CH1_BOT + 0x8 * ch_num);
- }
- }
- bool dcss_dtg_global_alpha_changed(struct dcss_dtg *dtg, int ch_num, int alpha)
- {
- if (ch_num)
- return false;
- return alpha != dtg->alpha;
- }
- void dcss_dtg_plane_alpha_set(struct dcss_dtg *dtg, int ch_num,
- const struct drm_format_info *format, int alpha)
- {
- /* we care about alpha only when channel 0 is concerned */
- if (ch_num)
- return;
- /*
- * Use global alpha if pixel format does not have alpha channel or the
- * user explicitly chose to use global alpha (i.e. alpha is not OPAQUE).
- */
- if (!format->has_alpha || alpha != 255)
- dtg->alpha_cfg = (alpha << DEFAULT_FG_ALPHA_POS) & DEFAULT_FG_ALPHA_MASK;
- else /* use per-pixel alpha otherwise */
- dtg->alpha_cfg = CH1_ALPHA_SEL;
- dtg->alpha = alpha;
- }
- void dcss_dtg_css_set(struct dcss_dtg *dtg)
- {
- dtg->control_status |=
- (0x5 << CSS_PIX_COMP_SWAP_POS) & CSS_PIX_COMP_SWAP_MASK;
- }
- void dcss_dtg_enable(struct dcss_dtg *dtg)
- {
- dtg->control_status |= DTG_START;
- dtg->control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
- dtg->control_status |= dtg->alpha_cfg;
- dcss_dtg_write(dtg, dtg->control_status, DCSS_DTG_TC_CONTROL_STATUS);
- dtg->in_use = true;
- }
- void dcss_dtg_shutoff(struct dcss_dtg *dtg)
- {
- dtg->control_status &= ~DTG_START;
- dcss_writel(dtg->control_status,
- dtg->base_reg + DCSS_DTG_TC_CONTROL_STATUS);
- dtg->in_use = false;
- }
- bool dcss_dtg_is_enabled(struct dcss_dtg *dtg)
- {
- return dtg->in_use;
- }
- void dcss_dtg_ch_enable(struct dcss_dtg *dtg, int ch_num, bool en)
- {
- u32 ch_en_map[] = {CH1_EN, CH2_EN, CH3_EN};
- u32 control_status;
- control_status = dtg->control_status & ~ch_en_map[ch_num];
- control_status |= en ? ch_en_map[ch_num] : 0;
- control_status &= ~(CH1_ALPHA_SEL | DEFAULT_FG_ALPHA_MASK);
- control_status |= dtg->alpha_cfg;
- if (dtg->control_status != control_status)
- dcss_dtg_write(dtg, control_status, DCSS_DTG_TC_CONTROL_STATUS);
- dtg->control_status = control_status;
- }
- void dcss_dtg_vblank_irq_enable(struct dcss_dtg *dtg, bool en)
- {
- u32 status;
- u32 mask = en ? LINE1_IRQ : 0;
- if (en) {
- status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
- dcss_writel(status & LINE1_IRQ,
- dtg->base_reg + DCSS_DTG_INT_CONTROL);
- }
- dcss_update(mask, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
- }
- void dcss_dtg_ctxld_kick_irq_enable(struct dcss_dtg *dtg, bool en)
- {
- u32 status;
- u32 mask = en ? LINE0_IRQ : 0;
- if (en) {
- status = dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS);
- if (!dtg->ctxld_kick_irq_en) {
- dcss_writel(status & LINE0_IRQ,
- dtg->base_reg + DCSS_DTG_INT_CONTROL);
- enable_irq(dtg->ctxld_kick_irq);
- dtg->ctxld_kick_irq_en = true;
- dcss_update(mask, LINE0_IRQ,
- dtg->base_reg + DCSS_DTG_INT_MASK);
- }
- return;
- }
- if (!dtg->ctxld_kick_irq_en)
- return;
- disable_irq_nosync(dtg->ctxld_kick_irq);
- dtg->ctxld_kick_irq_en = false;
- dcss_update(mask, LINE0_IRQ, dtg->base_reg + DCSS_DTG_INT_MASK);
- }
- void dcss_dtg_vblank_irq_clear(struct dcss_dtg *dtg)
- {
- dcss_update(LINE1_IRQ, LINE1_IRQ, dtg->base_reg + DCSS_DTG_INT_CONTROL);
- }
- bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
- {
- return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
- }
|