summaryrefslogtreecommitdiff
path: root/drivers/gpu/ipu-v3/ipu-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/ipu-v3/ipu-common.c')
-rw-r--r--drivers/gpu/ipu-v3/ipu-common.c131
1 files changed, 131 insertions, 0 deletions
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 9d3584b3b315..891cbefe2ad4 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,137 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
}
EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
+
+/* Frame Synchronization Unit Channel Linking */
+
+struct fsu_link_reg_info {
+ int chno;
+ u32 reg;
+ u32 mask;
+ u32 val;
+};
+
+struct fsu_link_info {
+ struct fsu_link_reg_info src;
+ struct fsu_link_reg_info sink;
+};
+
+static const struct fsu_link_info fsu_link_info[] = {
+ {
+ .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
+ FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
+ .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
+ FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
+ }, {
+ .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
+ FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
+ .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
+ FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
+ }, {
+ .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
+ FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
+ .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
+ FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
+ }, {
+ .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 },
+ .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
+ FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
+ },
+};
+
+static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
+ if (src == fsu_link_info[i].src.chno &&
+ sink == fsu_link_info[i].sink.chno)
+ return &fsu_link_info[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * Links a source channel to a sink channel in the FSU.
+ */
+int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
+{
+ const struct fsu_link_info *link;
+ u32 src_reg, sink_reg;
+ unsigned long flags;
+
+ link = find_fsu_link_info(src_ch, sink_ch);
+ if (!link)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ if (link->src.mask) {
+ src_reg = ipu_cm_read(ipu, link->src.reg);
+ src_reg &= ~link->src.mask;
+ src_reg |= link->src.val;
+ ipu_cm_write(ipu, src_reg, link->src.reg);
+ }
+
+ if (link->sink.mask) {
+ sink_reg = ipu_cm_read(ipu, link->sink.reg);
+ sink_reg &= ~link->sink.mask;
+ sink_reg |= link->sink.val;
+ ipu_cm_write(ipu, sink_reg, link->sink.reg);
+ }
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_fsu_link);
+
+/*
+ * Unlinks source and sink channels in the FSU.
+ */
+int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
+{
+ const struct fsu_link_info *link;
+ u32 src_reg, sink_reg;
+ unsigned long flags;
+
+ link = find_fsu_link_info(src_ch, sink_ch);
+ if (!link)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ if (link->src.mask) {
+ src_reg = ipu_cm_read(ipu, link->src.reg);
+ src_reg &= ~link->src.mask;
+ ipu_cm_write(ipu, src_reg, link->src.reg);
+ }
+
+ if (link->sink.mask) {
+ sink_reg = ipu_cm_read(ipu, link->sink.reg);
+ sink_reg &= ~link->sink.mask;
+ ipu_cm_write(ipu, sink_reg, link->sink.reg);
+ }
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
+
+/* Link IDMAC channels in the FSU */
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+ return ipu_fsu_link(src->ipu, src->num, sink->num);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_link);
+
+/* Unlink IDMAC channels in the FSU */
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+ return ipu_fsu_unlink(src->ipu, src->num, sink->num);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
+
struct ipu_devtype {
const char *name;
unsigned long cm_ofs;