blob: ebef91995e83f2db9b28a3f942931a2243e2234a [file] [log] [blame]
/*
* Copyright 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/io.h>
#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <soc/imx8/soc.h>
#include "dcss-prv.h"
#include <video/imx-dcss.h>
#define DCSS_BLKCTL_RESET_CTRL 0x00
#define B_CLK_RESETN BIT(0)
#define APB_CLK_RESETN BIT(1)
#define P_CLK_RESETN BIT(2)
#define RTR_CLK_RESETN BIT(3)
#define HDMI_RESETN BIT(4)
#define DCSS_BLKCTL_CONTROL0 0x10
#define HDMI_MIPI_CLK_SEL BIT(0)
#define DISPMIX_REFCLK_SEL_POS 4
#define DISPMIX_REFCLK_SEL_MASK GENMASK(5, 4)
#define DISPMIX_PIXCLK_SEL BIT(8)
#define HDMI_SRC_SECURE_EN BIT(16)
#define B0_SILICON_ID 0x20
static struct dcss_debug_reg blkctl_debug_reg[] = {
DCSS_DBG_REG(DCSS_BLKCTL_RESET_CTRL),
DCSS_DBG_REG(DCSS_BLKCTL_CONTROL0),
};
struct dcss_blkctl_priv {
struct dcss_soc *dcss;
void __iomem *base_reg;
bool hdmi_output;
u32 clk_setting;
};
#ifdef CONFIG_DEBUG_FS
void dcss_blkctl_dump_regs(struct seq_file *s, void *data)
{
struct dcss_soc *dcss = data;
int j;
seq_puts(s, ">> Dumping BLKCTL:\n");
for (j = 0; j < ARRAY_SIZE(blkctl_debug_reg); j++)
seq_printf(s, "%-35s(0x%04x) -> 0x%08x\n",
blkctl_debug_reg[j].name,
blkctl_debug_reg[j].ofs,
dcss_readl(dcss->blkctl_priv->base_reg +
blkctl_debug_reg[j].ofs));
}
#endif
static void dcss_blkctl_clk_reset(struct dcss_blkctl_priv *blkctl,
u32 assert, u32 deassert)
{
if (assert)
dcss_clr(assert, blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL);
if (deassert)
dcss_set(deassert, blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL);
}
void dcss_blkctl_cfg(struct dcss_soc *dcss)
{
struct dcss_blkctl_priv *blkctl = dcss->blkctl_priv;
if (blkctl->hdmi_output)
dcss_writel(blkctl->clk_setting,
blkctl->base_reg + DCSS_BLKCTL_CONTROL0);
else
dcss_writel((blkctl->clk_setting ^ HDMI_MIPI_CLK_SEL) |
DISPMIX_PIXCLK_SEL,
blkctl->base_reg + DCSS_BLKCTL_CONTROL0);
/* deassert clock domains resets */
dcss_blkctl_clk_reset(blkctl, 0, 0xffffff);
}
int dcss_blkctl_init(struct dcss_soc *dcss, unsigned long blkctl_base)
{
struct device_node *node = dcss->dev->of_node;
int len;
const char *disp_dev;
struct dcss_blkctl_priv *blkctl;
blkctl = devm_kzalloc(dcss->dev, sizeof(*blkctl), GFP_KERNEL);
if (!blkctl)
return -ENOMEM;
blkctl->base_reg = devm_ioremap(dcss->dev, blkctl_base, SZ_4K);
if (!blkctl->base_reg) {
dev_err(dcss->dev, "unable to remap BLK CTRL base\n");
return -ENOMEM;
}
blkctl->dcss = dcss;
dcss->blkctl_priv = blkctl;
disp_dev = of_get_property(node, "disp-dev", &len);
if (!disp_dev || !strncmp(disp_dev, "hdmi_disp", 9))
blkctl->hdmi_output = true;
if (imx8_get_soc_revision() >= B0_SILICON_ID)
blkctl->clk_setting = HDMI_MIPI_CLK_SEL;
dcss_blkctl_cfg(dcss);
return 0;
}
void dcss_blkctl_exit(struct dcss_soc *dcss)
{
/* assert clock domains resets */
dcss_blkctl_clk_reset(dcss->blkctl_priv,
B_CLK_RESETN | APB_CLK_RESETN | P_CLK_RESETN |
HDMI_RESETN | RTR_CLK_RESETN, 0);
}
/* disabled only by cold reset/reboot */
void dcss_blkctl_hdmi_secure_src_en(struct dcss_soc *dcss)
{
struct dcss_blkctl_priv *blkctl = dcss->blkctl_priv;
dcss_set(HDMI_SRC_SECURE_EN, blkctl->base_reg + DCSS_BLKCTL_CONTROL0);
}
EXPORT_SYMBOL(dcss_blkctl_hdmi_secure_src_en);