blob: 66df49e565df6d4c92d1cb186edb5a72ccfe4d42 [file] [log] [blame]
/*
* Copyright (C) 2017 NXP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <linux/device.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <video/videomode.h>
#include <drm/drm_fourcc.h>
#include <video/imx-dcss.h>
#include "dcss-prv.h"
#define USE_CTXLD
#define DCSS_SS_SYS_CTRL 0x00
#define RUN_EN BIT(0)
#define DCSS_SS_DISPLAY 0x10
#define LRC_X_POS 0
#define LRC_X_MASK GENMASK(12, 0)
#define LRC_Y_POS 16
#define LRC_Y_MASK GENMASK(28, 16)
#define DCSS_SS_HSYNC 0x20
#define DCSS_SS_VSYNC 0x30
#define SYNC_START_POS 0
#define SYNC_START_MASK GENMASK(12, 0)
#define SYNC_END_POS 16
#define SYNC_END_MASK GENMASK(28, 16)
#define SYNC_POL BIT(31)
#define DCSS_SS_DE_ULC 0x40
#define ULC_X_POS 0
#define ULC_X_MASK GENMASK(12, 0)
#define ULC_Y_POS 16
#define ULC_Y_MASK GENMASK(28, 16)
#define ULC_POL BIT(31)
#define DCSS_SS_DE_LRC 0x50
#define DCSS_SS_MODE 0x60
#define PIPE_MODE_POS 0
#define PIPE_MODE_MASK GENMASK(1, 0)
#define DCSS_SS_COEFF 0x70
#define HORIZ_A_POS 0
#define HORIZ_A_MASK GENMASK(3, 0)
#define HORIZ_B_POS 4
#define HORIZ_B_MASK GENMASK(7, 4)
#define HORIZ_C_POS 8
#define HORIZ_C_MASK GENMASK(11, 8)
#define HORIZ_H_NORM_POS 12
#define HORIZ_H_NORM_MASK GENMASK(14, 12)
#define VERT_A_POS 16
#define VERT_A_MASK GENMASK(19, 16)
#define VERT_B_POS 20
#define VERT_B_MASK GENMASK(23, 20)
#define VERT_C_POS 24
#define VERT_C_MASK GENMASK(27, 24)
#define VERT_H_NORM_POS 28
#define VERT_H_NORM_MASK GENMASK(30, 28)
#define DCSS_SS_CLIP_CB 0x80
#define DCSS_SS_CLIP_CR 0x90
#define CLIP_MIN_POS 0
#define CLIP_MIN_MASK GENMASK(9, 0)
#define CLIP_MAX_POS 0
#define CLIP_MAX_MASK GENMASK(23, 16)
#define DCSS_SS_INTER_MODE 0xA0
#define INT_EN BIT(0)
#define VSYNC_SHIFT BIT(1)
static struct dcss_debug_reg ss_debug_reg[] = {
DCSS_DBG_REG(DCSS_SS_SYS_CTRL),
DCSS_DBG_REG(DCSS_SS_DISPLAY),
DCSS_DBG_REG(DCSS_SS_HSYNC),
DCSS_DBG_REG(DCSS_SS_VSYNC),
DCSS_DBG_REG(DCSS_SS_DE_ULC),
DCSS_DBG_REG(DCSS_SS_DE_LRC),
DCSS_DBG_REG(DCSS_SS_MODE),
DCSS_DBG_REG(DCSS_SS_COEFF),
DCSS_DBG_REG(DCSS_SS_CLIP_CB),
DCSS_DBG_REG(DCSS_SS_CLIP_CR),
DCSS_DBG_REG(DCSS_SS_INTER_MODE),
};
struct dcss_ss_priv {
struct dcss_soc *dcss;
void __iomem *base_reg;
u32 base_ofs;
u32 ctx_id;
bool in_use;
};
static void dcss_ss_write(struct dcss_ss_priv *ss, u32 val, u32 ofs)
{
if (!ss->in_use)
dcss_writel(val, ss->base_reg + ofs);
#if defined(USE_CTXLD)
dcss_ctxld_write(ss->dcss, ss->ctx_id, val,
ss->base_ofs + ofs);
#endif
}
#ifdef CONFIG_DEBUG_FS
void dcss_ss_dump_regs(struct seq_file *s, void *data)
{
struct dcss_soc *dcss = data;
int j;
seq_puts(s, ">> Dumping SUBSAM:\n");
for (j = 0; j < ARRAY_SIZE(ss_debug_reg); j++)
seq_printf(s, "%-35s(0x%04x) -> 0x%08x\n",
ss_debug_reg[j].name,
ss_debug_reg[j].ofs,
dcss_readl(dcss->ss_priv->base_reg +
ss_debug_reg[j].ofs));
}
#endif
int dcss_ss_init(struct dcss_soc *dcss, unsigned long ss_base)
{
struct dcss_ss_priv *ss;
ss = devm_kzalloc(dcss->dev, sizeof(*ss), GFP_KERNEL);
if (!ss)
return -ENOMEM;
dcss->ss_priv = ss;
ss->dcss = dcss;
ss->base_reg = devm_ioremap(dcss->dev, ss_base, SZ_4K);
if (!ss->base_reg) {
dev_err(dcss->dev, "ss: unable to remap ss base\n");
return -ENOMEM;
}
ss->base_ofs = ss_base;
#if defined(USE_CTXLD)
ss->ctx_id = CTX_SB_HP;
#endif
return 0;
}
void dcss_ss_exit(struct dcss_soc *dcss)
{
dcss_writel(0, dcss->ss_priv->base_reg + DCSS_SS_SYS_CTRL);
}
void dcss_ss_subsam_set(struct dcss_soc *dcss, u32 pix_format)
{
if (pix_format == DRM_FORMAT_P010) {
dcss_ss_write(dcss->ss_priv, 0x21612161, DCSS_SS_COEFF);
dcss_ss_write(dcss->ss_priv, 2, DCSS_SS_MODE);
dcss_ss_write(dcss->ss_priv, 0x03c00040, DCSS_SS_CLIP_CB);
dcss_ss_write(dcss->ss_priv, 0x03c00040, DCSS_SS_CLIP_CR);
return;
}
dcss_ss_write(dcss->ss_priv, 0x41614161, DCSS_SS_COEFF);
dcss_ss_write(dcss->ss_priv, 0, DCSS_SS_MODE);
dcss_ss_write(dcss->ss_priv, 0x03ff0000, DCSS_SS_CLIP_CB);
dcss_ss_write(dcss->ss_priv, 0x03ff0000, DCSS_SS_CLIP_CR);
}
void dcss_ss_sync_set(struct dcss_soc *dcss, struct videomode *vm,
bool phsync, bool pvsync)
{
struct dcss_ss_priv *ss = dcss->ss_priv;
u16 lrc_x, lrc_y;
u16 hsync_start, hsync_end;
u16 vsync_start, vsync_end;
u16 de_ulc_x, de_ulc_y;
u16 de_lrc_x, de_lrc_y;
lrc_x = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
vm->hactive - 1;
lrc_y = vm->vfront_porch + vm->vback_porch + vm->vsync_len +
vm->vactive - 1;
dcss_ss_write(ss, (lrc_y << LRC_Y_POS) | lrc_x, DCSS_SS_DISPLAY);
hsync_start = vm->hfront_porch + vm->hback_porch + vm->hsync_len +
vm->hactive - 1;
hsync_end = vm->hsync_len - 1;
dcss_ss_write(ss, (phsync ? SYNC_POL : 0) |
((u32)hsync_end << SYNC_END_POS) | hsync_start,
DCSS_SS_HSYNC);
vsync_start = vm->vfront_porch - 1;
vsync_end = vm->vfront_porch + vm->vsync_len - 1;
dcss_ss_write(ss, (pvsync ? SYNC_POL : 0) |
((u32)vsync_end << SYNC_END_POS) | vsync_start,
DCSS_SS_VSYNC);
de_ulc_x = vm->hsync_len + vm->hback_porch - 1;
de_ulc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch;
dcss_ss_write(ss, SYNC_POL | ((u32)de_ulc_y << ULC_Y_POS) | de_ulc_x,
DCSS_SS_DE_ULC);
de_lrc_x = vm->hsync_len + vm->hback_porch + vm->hactive - 1;
de_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
vm->vactive - 1;
dcss_ss_write(ss, (de_lrc_y << LRC_Y_POS) | de_lrc_x, DCSS_SS_DE_LRC);
}
EXPORT_SYMBOL(dcss_ss_sync_set);
void dcss_ss_enable(struct dcss_soc *dcss, bool en)
{
struct dcss_ss_priv *ss = dcss->ss_priv;
dcss_ss_write(ss, en ? RUN_EN : 0, DCSS_SS_SYS_CTRL);
}
EXPORT_SYMBOL(dcss_ss_enable);