mirror of https://github.com/armbian/build.git
486 lines
14 KiB
Diff
486 lines
14 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|
Date: Sat, 25 Apr 2020 22:13:51 +0200
|
|
Subject: drm/meson: Meson8/Meson8b/Meson8m2 VCLK - HACK
|
|
|
|
WiP
|
|
|
|
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|
---
|
|
drivers/gpu/drm/meson/meson_drv.c | 101 ++++++-
|
|
drivers/gpu/drm/meson/meson_drv.h | 32 ++
|
|
drivers/gpu/drm/meson/meson_vclk.c | 146 ++++++++++
|
|
drivers/gpu/drm/meson/meson_venc.c | 24 +-
|
|
4 files changed, 293 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/gpu/drm/meson/meson_drv.c
|
|
+++ b/drivers/gpu/drm/meson/meson_drv.c
|
|
@@ -170,6 +170,35 @@ static void meson_vpu_init(struct meson_drm *priv)
|
|
}
|
|
}
|
|
|
|
+static int meson_video_clock_init(struct meson_drm *priv)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = clk_bulk_prepare(VPU_VID_CLK_NUM, priv->vid_clks);
|
|
+ if (ret)
|
|
+ return dev_err_probe(priv->dev, ret,
|
|
+ "Failed to prepare the video clocks\n");
|
|
+
|
|
+ ret = clk_bulk_prepare(priv->num_intr_clks, priv->intr_clks);
|
|
+ if (ret)
|
|
+ return dev_err_probe(priv->dev, ret,
|
|
+ "Failed to prepare the interrupt clocks\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void meson_video_clock_exit(struct meson_drm *priv)
|
|
+{
|
|
+ if (priv->clk_dac_enabled)
|
|
+ clk_disable(priv->clk_dac);
|
|
+
|
|
+ if (priv->clk_venc_enabled)
|
|
+ clk_disable(priv->clk_venc);
|
|
+
|
|
+ clk_bulk_unprepare(priv->num_intr_clks, priv->intr_clks);
|
|
+ clk_bulk_unprepare(VPU_VID_CLK_NUM, priv->vid_clks);
|
|
+}
|
|
+
|
|
static void meson_fbdev_setup(struct meson_drm *priv)
|
|
{
|
|
unsigned int preferred_bpp;
|
|
@@ -263,10 +292,59 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
|
|
priv->compat = match->compat;
|
|
priv->afbcd.ops = match->afbcd_ops;
|
|
|
|
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) {
|
|
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_PRE].id = "vid_pll_pre";
|
|
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_POST].id = "vid_pll_post";
|
|
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_SOFT_PRE].id = "vid_pll_soft_pre";
|
|
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_SOFT_POST].id = "vid_pll_soft_post";
|
|
+
|
|
+ ret = devm_reset_control_bulk_get_exclusive(dev,
|
|
+ VPU_RESET_VID_PLL_NUM,
|
|
+ priv->vid_pll_resets);
|
|
+ if (ret)
|
|
+ goto free_drm;
|
|
+
|
|
+ priv->intr_clks[0].id = "vpu_intr";
|
|
+ priv->intr_clks[1].id = "hdmi_intr_sync";
|
|
+ priv->intr_clks[2].id = "venci_int";
|
|
+ priv->num_intr_clks = 3;
|
|
+
|
|
+ ret = devm_clk_bulk_get(dev, priv->num_intr_clks,
|
|
+ priv->intr_clks);
|
|
+ if (ret)
|
|
+ goto free_drm;
|
|
+
|
|
+ priv->vid_clks[VPU_VID_CLK_TMDS].id = "tmds";
|
|
+ priv->vid_clks[VPU_VID_CLK_HDMI_TX_PIXEL].id = "hdmi_tx_pixel";
|
|
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCP].id = "cts_encp";
|
|
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCI].id = "cts_enci";
|
|
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCT].id = "cts_enct";
|
|
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCL].id = "cts_encl";
|
|
+ priv->vid_clks[VPU_VID_CLK_CTS_VDAC0].id = "cts_vdac0";
|
|
+
|
|
+ ret = devm_clk_bulk_get(dev, VPU_VID_CLK_NUM, priv->vid_clks);
|
|
+ if (ret)
|
|
+ goto free_drm;
|
|
+ } else {
|
|
+ priv->intr_clks[0].id = "vpu_intr";
|
|
+ priv->num_intr_clks = 1;
|
|
+
|
|
+ ret = devm_clk_bulk_get_optional(dev, priv->num_intr_clks,
|
|
+ priv->intr_clks);
|
|
+ if (ret)
|
|
+ goto free_drm;
|
|
+ }
|
|
+
|
|
+ ret = meson_video_clock_init(priv);
|
|
+ if (ret)
|
|
+ goto free_drm;
|
|
+
|
|
regs = devm_platform_ioremap_resource_byname(pdev, "vpu");
|
|
if (IS_ERR(regs)) {
|
|
ret = PTR_ERR(regs);
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
}
|
|
|
|
priv->io_base = regs;
|
|
@@ -281,7 +359,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
|
|
regs = devm_ioremap(dev, res->start, resource_size(res));
|
|
if (!regs) {
|
|
ret = -EADDRNOTAVAIL;
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
}
|
|
|
|
priv->hhi = devm_regmap_init_mmio(dev, regs,
|
|
@@ -290,36 +368,36 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
|
|
dev_err(&pdev->dev,
|
|
"Couldn't create the HHI regmap\n");
|
|
ret = PTR_ERR(priv->hhi);
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
}
|
|
}
|
|
|
|
priv->canvas = meson_canvas_get(dev);
|
|
if (IS_ERR(priv->canvas)) {
|
|
ret = PTR_ERR(priv->canvas);
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
}
|
|
|
|
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
|
|
if (ret)
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
|
|
if (ret) {
|
|
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
}
|
|
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
|
|
if (ret) {
|
|
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
|
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
}
|
|
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
|
|
if (ret) {
|
|
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
|
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
|
|
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
|
|
- goto free_drm;
|
|
+ goto video_clock_exit;
|
|
}
|
|
|
|
priv->vsync_irq = platform_get_irq(pdev, 0);
|
|
@@ -425,6 +503,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
|
|
exit_afbcd:
|
|
if (priv->afbcd.ops)
|
|
priv->afbcd.ops->exit(priv);
|
|
+video_clock_exit:
|
|
+ meson_video_clock_exit(priv);
|
|
free_drm:
|
|
drm_dev_put(drm);
|
|
|
|
@@ -469,6 +549,8 @@ static void meson_drv_unbind(struct device *dev)
|
|
|
|
if (priv->afbcd.ops)
|
|
priv->afbcd.ops->exit(priv);
|
|
+
|
|
+ meson_video_clock_exit(priv);
|
|
}
|
|
|
|
static const struct component_master_ops meson_drv_master_ops = {
|
|
@@ -483,6 +565,8 @@ static int __maybe_unused meson_drv_pm_suspend(struct device *dev)
|
|
if (!priv)
|
|
return 0;
|
|
|
|
+ // TODO: video clock suspend
|
|
+
|
|
return drm_mode_config_helper_suspend(priv->drm);
|
|
}
|
|
|
|
@@ -493,6 +577,7 @@ static int __maybe_unused meson_drv_pm_resume(struct device *dev)
|
|
if (!priv)
|
|
return 0;
|
|
|
|
+ meson_video_clock_init(priv);
|
|
meson_vpu_init(priv);
|
|
meson_venc_init(priv);
|
|
meson_vpp_init(priv);
|
|
diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/gpu/drm/meson/meson_drv.h
|
|
+++ b/drivers/gpu/drm/meson/meson_drv.h
|
|
@@ -7,9 +7,11 @@
|
|
#ifndef __MESON_DRV_H
|
|
#define __MESON_DRV_H
|
|
|
|
+#include <linux/clk.h>
|
|
#include <linux/device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/regmap.h>
|
|
+#include <linux/reset.h>
|
|
|
|
struct drm_crtc;
|
|
struct drm_device;
|
|
@@ -45,6 +47,25 @@ struct meson_drm_soc_limits {
|
|
unsigned int max_hdmi_phy_freq;
|
|
};
|
|
|
|
+enum vpu_bulk_clk_id {
|
|
+ VPU_VID_CLK_TMDS = 0,
|
|
+ VPU_VID_CLK_HDMI_TX_PIXEL,
|
|
+ VPU_VID_CLK_CTS_ENCP,
|
|
+ VPU_VID_CLK_CTS_ENCI,
|
|
+ VPU_VID_CLK_CTS_ENCT,
|
|
+ VPU_VID_CLK_CTS_ENCL,
|
|
+ VPU_VID_CLK_CTS_VDAC0,
|
|
+ VPU_VID_CLK_NUM
|
|
+};
|
|
+
|
|
+enum vpu_bulk_vid_pll_reset_id {
|
|
+ VPU_RESET_VID_PLL_PRE = 0,
|
|
+ VPU_RESET_VID_PLL_POST,
|
|
+ VPU_RESET_VID_PLL_SOFT_PRE,
|
|
+ VPU_RESET_VID_PLL_SOFT_POST,
|
|
+ VPU_RESET_VID_PLL_NUM
|
|
+};
|
|
+
|
|
struct meson_drm {
|
|
struct device *dev;
|
|
enum vpu_compatible compat;
|
|
@@ -70,6 +91,17 @@ struct meson_drm {
|
|
bool cvbs_dac_enabled;
|
|
struct platform_device *cvbs_dac_pdev;
|
|
|
|
+ struct clk_bulk_data intr_clks[3];
|
|
+ unsigned int num_intr_clks;
|
|
+ bool intr_clks_enabled;
|
|
+ struct clk_bulk_data vid_clks[VPU_VID_CLK_NUM];
|
|
+ bool vid_clk_rate_exclusive[VPU_VID_CLK_NUM];
|
|
+ struct clk *clk_venc;
|
|
+ bool clk_venc_enabled;
|
|
+ struct clk *clk_dac;
|
|
+ bool clk_dac_enabled;
|
|
+ struct reset_control_bulk_data vid_pll_resets[VPU_RESET_VID_PLL_NUM];
|
|
+
|
|
/* Components Data */
|
|
struct {
|
|
bool osd1_enabled;
|
|
diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/gpu/drm/meson/meson_vclk.c
|
|
+++ b/drivers/gpu/drm/meson/meson_vclk.c
|
|
@@ -732,6 +732,11 @@ meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq)
|
|
return MODE_CLOCK_HIGH;
|
|
}
|
|
|
|
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2))
|
|
+ return MODE_OK;
|
|
+
|
|
if (meson_hdmi_pll_find_params(priv, freq, &m, &frac, &od))
|
|
return MODE_OK;
|
|
|
|
@@ -784,6 +789,11 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
|
|
return MODE_CLOCK_HIGH;
|
|
}
|
|
|
|
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2))
|
|
+ return MODE_OK;
|
|
+
|
|
for (i = 0 ; params[i].pixel_freq ; ++i) {
|
|
DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n",
|
|
i, params[i].pixel_freq,
|
|
@@ -1024,6 +1034,128 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
|
|
regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN);
|
|
}
|
|
|
|
+static int meson_vclk_set_rate_exclusive(struct meson_drm *priv,
|
|
+ enum vpu_bulk_clk_id clk_id,
|
|
+ unsigned int rate_khz)
|
|
+{
|
|
+ struct clk *clk = priv->vid_clks[clk_id].clk;
|
|
+ int ret;
|
|
+
|
|
+ ret = clk_set_rate_exclusive(clk, rate_khz * 1000UL);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ priv->vid_clk_rate_exclusive[clk_id] = true;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void meson_vclk_disable_ccf(struct meson_drm *priv)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ /* allow all clocks to be changed in _enable again */
|
|
+ for (i = 0; i < VPU_VID_CLK_NUM; i++) {
|
|
+ if (!priv->vid_clk_rate_exclusive[i])
|
|
+ continue;
|
|
+
|
|
+ clk_rate_exclusive_put(priv->vid_clks[i].clk);
|
|
+ priv->vid_clk_rate_exclusive[i] = false;
|
|
+ }
|
|
+
|
|
+ if (priv->clk_dac_enabled) {
|
|
+ clk_disable(priv->clk_dac);
|
|
+ priv->clk_dac_enabled = false;
|
|
+ }
|
|
+
|
|
+ if (priv->clk_venc_enabled) {
|
|
+ clk_disable(priv->clk_venc);
|
|
+ priv->clk_venc_enabled = false;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int meson_vclk_enable_ccf(struct meson_drm *priv, unsigned int target,
|
|
+ bool hdmi_use_enci, unsigned int phy_freq,
|
|
+ unsigned int dac_freq, unsigned int venc_freq)
|
|
+{
|
|
+ enum vpu_bulk_clk_id venc_clk_id, dac_clk_id;
|
|
+ int ret;
|
|
+
|
|
+ if (target == MESON_VCLK_TARGET_CVBS || hdmi_use_enci)
|
|
+ venc_clk_id = VPU_VID_CLK_CTS_ENCI;
|
|
+ else
|
|
+ venc_clk_id = VPU_VID_CLK_CTS_ENCP;
|
|
+
|
|
+ if (target == MESON_VCLK_TARGET_CVBS)
|
|
+ dac_clk_id = VPU_VID_CLK_CTS_VDAC0;
|
|
+ else
|
|
+ dac_clk_id = VPU_VID_CLK_HDMI_TX_PIXEL;
|
|
+
|
|
+ /*
|
|
+ * The TMDS clock also updates the PLL. Protect the PLL rate so all
|
|
+ * following clocks are derived from the PLL setting which matches the
|
|
+ * TMDS clock.
|
|
+ */
|
|
+ ret = meson_vclk_set_rate_exclusive(priv, VPU_VID_CLK_TMDS, phy_freq);
|
|
+ if (ret) {
|
|
+ dev_err(priv->dev, "Failed to set TMDS clock to %ukHz: %d\n",
|
|
+ phy_freq, ret);
|
|
+ goto out_enable_clocks;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The DAC clock may be derived from a parent of the VENC clock so we
|
|
+ * must protect the VENC clock from changing it's rate. This works
|
|
+ * because the DAC freq can be divided by the VENC clock.
|
|
+ */
|
|
+ ret = meson_vclk_set_rate_exclusive(priv, venc_clk_id, venc_freq);
|
|
+ if (ret) {
|
|
+ dev_warn(priv->dev,
|
|
+ "Failed to set VENC clock to %ukHz while TMDS clock is %ukHz: %d\n",
|
|
+ venc_freq, phy_freq, ret);
|
|
+ goto out_enable_clocks;
|
|
+ }
|
|
+
|
|
+ priv->clk_venc = priv->vid_clks[venc_clk_id].clk;
|
|
+
|
|
+ /*
|
|
+ * after changing any of the VID_PLL_* clocks (which can happen when
|
|
+ * update the VENC clock rate) we need to assert and then de-assert the
|
|
+ * VID_DIVIDER_CNTL_* reset lines.
|
|
+ */
|
|
+ reset_control_bulk_assert(VPU_RESET_VID_PLL_NUM, priv->vid_pll_resets);
|
|
+ reset_control_bulk_deassert(VPU_RESET_VID_PLL_NUM, priv->vid_pll_resets);
|
|
+
|
|
+ ret = meson_vclk_set_rate_exclusive(priv, dac_clk_id, dac_freq);
|
|
+ if (ret) {
|
|
+ dev_warn(priv->dev,
|
|
+ "Failed to set pixel clock to %ukHz while TMDS clock is %ukHz: %d\n",
|
|
+ dac_freq, phy_freq, ret);
|
|
+ goto out_enable_clocks;
|
|
+ }
|
|
+
|
|
+ priv->clk_dac = priv->vid_clks[dac_clk_id].clk;
|
|
+
|
|
+out_enable_clocks:
|
|
+ ret = clk_enable(priv->clk_venc);
|
|
+ if (ret)
|
|
+ dev_err(priv->dev,
|
|
+ "Failed to re-enable the VENC clock at %ukHz: %d\n",
|
|
+ venc_freq, ret);
|
|
+ else
|
|
+ priv->clk_venc_enabled = true;
|
|
+
|
|
+ ret = clk_enable(priv->clk_dac);
|
|
+ if (ret)
|
|
+ dev_err(priv->dev,
|
|
+ "Failed to re-enable the pixel clock at %ukHz: %d\n",
|
|
+ dac_freq, ret);
|
|
+ else
|
|
+ priv->clk_dac_enabled = true;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
|
unsigned int phy_freq, unsigned int vclk_freq,
|
|
unsigned int venc_freq, unsigned int dac_freq,
|
|
@@ -1034,6 +1166,20 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
|
unsigned int hdmi_tx_div;
|
|
unsigned int venc_div;
|
|
|
|
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
|
|
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) {
|
|
+ /* CVBS video clocks are generated off a 1296MHz base clock */
|
|
+ if (target == MESON_VCLK_TARGET_CVBS)
|
|
+ phy_freq = 1296000;
|
|
+
|
|
+ dev_err(priv->dev, "%s(target: %u, phy: %u, dac: %u, venc: %u, hdmi_use_enci: %u)\n", __func__, target, phy_freq, dac_freq, venc_freq, hdmi_use_enci);
|
|
+ meson_vclk_disable_ccf(priv);
|
|
+ meson_vclk_enable_ccf(priv, target, hdmi_use_enci, phy_freq,
|
|
+ dac_freq, venc_freq);
|
|
+ return;
|
|
+ }
|
|
+
|
|
if (target == MESON_VCLK_TARGET_CVBS) {
|
|
meson_venci_cvbs_clock_config(priv);
|
|
return;
|
|
diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/gpu/drm/meson/meson_venc.c
|
|
+++ b/drivers/gpu/drm/meson/meson_venc.c
|
|
@@ -1954,14 +1954,34 @@ void meson_venc_enable_vsync(struct meson_drm *priv)
|
|
priv->io_base + _REG(VENC_INTCTRL));
|
|
}
|
|
|
|
- if (priv->hhi)
|
|
+ if (priv->intr_clks[0].clk) {
|
|
+ if (!priv->intr_clks_enabled) {
|
|
+ int ret;
|
|
+
|
|
+ ret = clk_bulk_enable(priv->num_intr_clks,
|
|
+ priv->intr_clks);
|
|
+ if (ret)
|
|
+ dev_err(priv->dev,
|
|
+ "Failed to enable the interrupt clocks\n");
|
|
+ else
|
|
+ priv->intr_clks_enabled = true;
|
|
+ }
|
|
+ } else {
|
|
regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25));
|
|
+ }
|
|
}
|
|
|
|
void meson_venc_disable_vsync(struct meson_drm *priv)
|
|
{
|
|
- if (priv->hhi)
|
|
+ if (priv->intr_clks[0].clk) {
|
|
+ if (priv->intr_clks_enabled) {
|
|
+ clk_bulk_disable(priv->num_intr_clks,
|
|
+ priv->intr_clks);
|
|
+ priv->intr_clks_enabled = false;
|
|
+ }
|
|
+ } else {
|
|
regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0);
|
|
+ }
|
|
|
|
writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL));
|
|
}
|
|
--
|
|
Armbian
|
|
|