#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/seq_file.h>
+#include <linux/delay.h>
#include <soc/imx8/soc.h>
#include "dcss-prv.h"
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;
if (imx8_get_soc_revision() == B0_SILICON_ID)
blkctl->clk_setting = HDMI_MIPI_CLK_SEL;
- 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, B_CLK_RESETN | APB_CLK_RESETN |
- P_CLK_RESETN | HDMI_RESETN | RTR_CLK_RESETN);
+ dcss_blkctl_cfg(dcss);
return 0;
}
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/busfreq-imx.h>
#include <video/imx-dcss.h>
#include <drm/drm_fourcc.h>
}
EXPORT_SYMBOL(dcss_vblank_irq_clear);
-static int dcss_submodules_init(struct dcss_soc *dcss, unsigned long dcss_base)
+static int dcss_submodules_init(struct dcss_soc *dcss)
{
int ret;
+ u32 dcss_base = dcss->start_addr;
ret = dcss_blkctl_init(dcss, dcss_base + dcss->devtype->blkctl_ofs);
if (ret)
return ret;
}
- ret = dcss_submodules_init(dcss, res->start);
+ dcss->start_addr = res->start;
+
+ ret = dcss_submodules_init(dcss);
if (ret) {
dev_err(&pdev->dev, "submodules initialization failed\n");
return ret;
dcss_debugfs_init(dcss);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ request_bus_freq(BUS_FREQ_HIGH);
+
return dcss_add_client_devices(dcss);
}
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int dcss_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dcss_soc *dcss = platform_get_drvdata(pdev);
+ int ret;
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ ret = dcss_ctxld_suspend(dcss);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(dcss->p_clk);
+
+ release_bus_freq(BUS_FREQ_HIGH);
+
+ return 0;
+}
+
+static int dcss_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dcss_soc *dcss = platform_get_drvdata(pdev);
+
+ request_bus_freq(BUS_FREQ_HIGH);
+
+ clk_prepare_enable(dcss->p_clk);
+
+ dcss_blkctl_cfg(dcss);
+ dcss_hdr10_cfg(dcss);
+
+ dcss_ctxld_resume(dcss);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int dcss_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dcss_soc *dcss = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = dcss_ctxld_suspend(dcss);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(dcss->p_clk);
+
+ release_bus_freq(BUS_FREQ_HIGH);
+
+ return 0;
+}
+
+static int dcss_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dcss_soc *dcss = platform_get_drvdata(pdev);
+
+ request_bus_freq(BUS_FREQ_HIGH);
+
+ clk_prepare_enable(dcss->p_clk);
+
+ dcss_blkctl_cfg(dcss);
+ dcss_hdr10_cfg(dcss);
+
+ dcss_ctxld_resume(dcss);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops dcss_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(dcss_suspend, dcss_resume)
+ SET_RUNTIME_PM_OPS(dcss_runtime_suspend,
+ dcss_runtime_resume, NULL)
+};
+
static const struct of_device_id dcss_dt_ids[] = {
{ .compatible = "nxp,imx8mq-dcss", .data = &dcss_type_imx8m, },
{ /* sentinel */ }
.driver = {
.name = "dcss-core",
.of_match_table = dcss_dt_ids,
+ .pm = &dcss_pm,
},
.probe = dcss_probe,
.remove = dcss_remove,
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/seq_file.h>
+#include <linux/delay.h>
#include <asm/cacheflush.h>
#include "video/imx-dcss.h"
struct dcss_soc *dcss;
void __iomem *ctxld_reg;
int irq;
+ bool irq_en;
struct dcss_ctxld_item *db[2];
struct dcss_ctxld_item *sb_hp[2];
return ctxld->irq;
}
- dcss_set(RD_ERR_EN | DB_COMP_EN | SB_HP_COMP_EN | SB_LP_COMP_EN |
- DB_PEND_SB_REC_EN | SB_PEND_DISP_ACTIVE_EN | AHB_ERR_EN |
- RD_ERR | AHB_ERR,
- ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
-
ret = devm_request_threaded_irq(dcss->dev, ctxld->irq,
dcss_ctxld_irq_handler,
dcss_ctxld_irq_handler_thread,
return ret;
}
+ ctxld->irq_en = true;
+
return 0;
}
+void dcss_ctxld_hw_cfg(struct dcss_soc *dcss)
+{
+ struct dcss_ctxld_priv *ctxld = dcss->ctxld_priv;
+
+ dcss_writel(RD_ERR_EN | DB_COMP_EN | SB_HP_COMP_EN | SB_LP_COMP_EN |
+ DB_PEND_SB_REC_EN | SB_PEND_DISP_ACTIVE_EN | AHB_ERR_EN |
+ RD_ERR | AHB_ERR,
+ ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
+}
+
/**
* dcss_ctxld_alloc_ctx - Allocate context memory.
*
return -ENOMEM;
}
- dcss_writel(0, priv->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
-
ret = dcss_ctxld_irq_config(priv);
if (!ret)
return ret;
+ dcss_ctxld_hw_cfg(dcss);
+
return 0;
}
mutex_unlock(&ctxld->mutex);
}
+int dcss_ctxld_resume(struct dcss_soc *dcss)
+{
+ struct dcss_ctxld_priv *ctxld = dcss->ctxld_priv;
+
+ dcss_ctxld_hw_cfg(dcss);
+
+ if (!ctxld->irq_en) {
+ enable_irq(dcss->ctxld_priv->irq);
+ ctxld->irq_en = true;
+ }
+
+ return 0;
+}
+
+int dcss_ctxld_suspend(struct dcss_soc *dcss)
+{
+ int ret = 0;
+ struct dcss_ctxld_priv *ctxld = dcss->ctxld_priv;
+ int wait_time_ms = 0;
+
+ while (ctxld->in_use && wait_time_ms < 500) {
+ msleep(20);
+ wait_time_ms += 20;
+ }
+
+ if (wait_time_ms > 500)
+ return -ETIMEDOUT;
+
+ mutex_lock(&ctxld->mutex);
+
+ if (ctxld->irq_en) {
+ disable_irq_nosync(dcss->ctxld_priv->irq);
+ ctxld->irq_en = false;
+ }
+
+ /* reset context region and sizes */
+ ctxld->current_ctx = 0;
+ ctxld->ctx_size[0][CTX_DB] = 0;
+ ctxld->ctx_size[0][CTX_SB_HP] = 0;
+ ctxld->ctx_size[0][CTX_SB_LP] = 0;
+
+ mutex_unlock(&ctxld->mutex);
+ return ret;
+}
+
#ifdef CONFIG_DEBUG_FS
void dcss_ctxld_dump(struct seq_file *s, void *data)
{
dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
vm->vactive - 1;
+ clk_disable_unprepare(dcss->p_clk);
+ clk_set_rate(dcss->p_clk, vm->pixelclock);
+ clk_prepare_enable(dcss->p_clk);
+
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_write(dtg,
((dis_ulc_y << TC_CTXLD_DB_Y_POS) & TC_CTXLD_DB_Y_MASK),
DCSS_DTG_TC_CTXLD);
-
- clk_set_rate(dcss->p_clk, vm->pixelclock);
}
EXPORT_SYMBOL(dcss_dtg_sync_set);
}
}
+void dcss_hdr10_cfg(struct dcss_soc *dcss)
+{
+ struct dcss_hdr10_priv *hdr10 = dcss->hdr10_priv;
+ struct dcss_hdr10_ch *ch;
+ int i;
+ u16 *lut;
+
+ for (i = 0; i < 4; i++) {
+ ch = &hdr10->ch[i];
+
+ lut = i < 3 ? dcss_hdr10_comp_lut : dcss_hdr10_opipe;
+
+ dcss_hdr10_lut_fill(dcss, i, 0, lut);
+ dcss_hdr10_lut_fill(dcss, i, 1, lut);
+ dcss_hdr10_lut_fill(dcss, i, 2, lut);
+
+ ch->old_out_cs = DCSS_COLORSPACE_UNKNOWN;
+ ch->old_in_cs = DCSS_COLORSPACE_UNKNOWN;
+ }
+}
+
static int dcss_hdr10_ch_init_all(struct dcss_soc *dcss,
unsigned long hdr10_base)
{
struct dcss_hdr10_priv *hdr10 = dcss->hdr10_priv;
struct dcss_hdr10_ch *ch;
int i;
- u16 *lut;
for (i = 0; i < 4; i++) {
ch = &hdr10->ch[i];
#if defined(USE_CTXLD)
ch->ctx_id = CTX_SB_HP;
#endif
-
- lut = i < 3 ? dcss_hdr10_comp_lut : dcss_hdr10_opipe;
-
- dcss_hdr10_lut_fill(dcss, i, 0, lut);
- dcss_hdr10_lut_fill(dcss, i, 1, lut);
- dcss_hdr10_lut_fill(dcss, i, 2, lut);
-
- ch->old_out_cs = DCSS_COLORSPACE_UNKNOWN;
- ch->old_in_cs = DCSS_COLORSPACE_UNKNOWN;
}
+#ifndef CONFIG_PM
+ dcss_hdr10_cfg(dcss);
+#endif
+
return 0;
}
struct device *dev;
const struct dcss_devtype *devtype;
+ u32 start_addr;
+
struct dcss_blkctl_priv *blkctl_priv;
struct dcss_ctxld_priv *ctxld_priv;
struct dcss_dpr_priv *dpr_priv;
/* BLKCTL */
int dcss_blkctl_init(struct dcss_soc *dcss, unsigned long blkctl_base);
+void dcss_blkctl_cfg(struct dcss_soc *dcss);
void dcss_blkctl_exit(struct dcss_soc *dcss);
/* CTXLD */
int dcss_ctxld_init(struct dcss_soc *dcss, unsigned long ctxld_base);
+void dcss_ctxld_hw_cfg(struct dcss_soc *dcss);
void dcss_ctxld_exit(struct dcss_soc *dcss);
void dcss_ctxld_write(struct dcss_soc *dcss, u32 ctx_id, u32 val, u32 reg_idx);
void dcss_ctxld_update(struct dcss_soc *dcss, u32 ctx_id, u32 val, u32 mask,
u32 reg_idx);
void dcss_ctxld_dump(struct seq_file *s, void *data);
+int dcss_ctxld_resume(struct dcss_soc *dcss);
+int dcss_ctxld_suspend(struct dcss_soc *dcss);
/* DPR */
int dcss_dpr_init(struct dcss_soc *dcss, unsigned long dpr_base);
/* HDR10 */
int dcss_hdr10_init(struct dcss_soc *dcss, unsigned long hdr10_base);
void dcss_hdr10_exit(struct dcss_soc *dcss);
+void dcss_hdr10_cfg(struct dcss_soc *dcss);
/* SCALER */
int dcss_scaler_init(struct dcss_soc *dcss, unsigned long scaler_base);