diff --git a/drivers/media/platform/uniphier/Makefile b/drivers/media/platform/uniphier/Makefile
index c3d67a148dbe..59be2edf0c53 100644
--- a/drivers/media/platform/uniphier/Makefile
+++ b/drivers/media/platform/uniphier/Makefile
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-uniphier-dvb-y += hsc-dma.o
+uniphier-dvb-y += hsc-dma.o hsc-css.o
 
 obj-$(CONFIG_DVB_UNIPHIER) += uniphier-dvb.o
diff --git a/drivers/media/platform/uniphier/hsc-css.c b/drivers/media/platform/uniphier/hsc-css.c
new file mode 100644
index 000000000000..0ab8156b0ffd
--- /dev/null
+++ b/drivers/media/platform/uniphier/hsc-css.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Socionext UniPhier DVB driver for High-speed Stream Controller (HSC).
+// CSS (Cross Stream Switch) connects MPEG2-TS input port and output port.
+//
+// Copyright (c) 2018 Socionext Inc.
+
+#include <linux/bitfield.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+
+#include "hsc.h"
+#include "hsc-reg.h"
+
+int hsc_css_out_to_ts_in(int out)
+{
+	if (out >= HSC_CSS_OUT_TSI0 && out <= HSC_CSS_OUT_TSI9)
+		return HSC_TS_IN0 + (out - HSC_CSS_OUT_TSI0);
+
+	return -1;
+}
+
+int hsc_css_out_to_dpll_src(int out)
+{
+	if (out >= HSC_CSS_OUT_TSI0 && out <= HSC_CSS_OUT_TSI9)
+		return HSC_DPLL_SRC_TSI0 + (out - HSC_CSS_OUT_TSI0);
+
+	return -1;
+}
+
+static bool css_in_is_valid(struct hsc_chip *chip, int in)
+{
+	return in < chip->spec->num_css_in;
+}
+
+static bool css_out_is_valid(struct hsc_chip *chip, int out)
+{
+	return out < chip->spec->num_css_out;
+}
+
+static const struct hsc_spec_css *css_in_get_spec(struct hsc_chip *chip,
+						  int in)
+{
+	const struct hsc_spec_css *spec = chip->spec->css_in;
+
+	if (!css_in_is_valid(chip, in))
+		return NULL;
+
+	return &spec[in];
+}
+
+static const struct hsc_spec_css *css_out_get_spec(struct hsc_chip *chip,
+						   int out)
+{
+	const struct hsc_spec_css *spec = chip->spec->css_out;
+
+	if (!css_out_is_valid(chip, out))
+		return NULL;
+
+	return &spec[out];
+}
+
+int hsc_dpll_get_src(struct hsc_chip *chip, int dpll, int *src)
+{
+	struct regmap *r = chip->regmap;
+	u32 v;
+
+	if (!src || dpll >= HSC_DPLL_NUM)
+		return -EINVAL;
+
+	regmap_read(r, CSS_DPCTRL(dpll), &v);
+	*src = ffs(v & CSS_DPCTRL_DPSEL_MASK) - 1;
+
+	return 0;
+}
+
+/**
+ * Select source clock of DPLL.
+ *
+ * @dpll: ID of DPLL, use one of HSC_DPLL_*
+ * @src : ID of clock source, use one of HSC_DPLL_SRC_* or HSC_DPLL_SRC_NONE
+ *        to disconnect
+ */
+int hsc_dpll_set_src(struct hsc_chip *chip, int dpll, int src)
+{
+	struct regmap *r = chip->regmap;
+	u32 v = 0;
+
+	if (dpll >= HSC_DPLL_NUM || src >= HSC_DPLL_SRC_NUM)
+		return -EINVAL;
+
+	if (src != HSC_DPLL_SRC_NONE)
+		v = 1 << src;
+
+	regmap_write(r, CSS_DPCTRL(dpll), v);
+
+	return 0;
+}
+
+static int hsc_css_get_polarity(struct hsc_chip *chip,
+				const struct hsc_reg_css_pol *pol,
+				bool *sync_bit, bool *val_bit, bool *clk_fall)
+{
+	struct regmap *r = chip->regmap;
+	u32 v;
+
+	if (!sync_bit || !val_bit || !clk_fall || !pol->valid)
+		return -EINVAL;
+
+	regmap_read(r, pol->reg, &v);
+
+	*sync_bit = !!(v & BIT(pol->sft_sync));
+	*val_bit = !!(v & BIT(pol->sft_val));
+	*clk_fall = !!(v & BIT(pol->sft_clk));
+
+	return 0;
+}
+
+/**
+ * Setup signal polarity of TS signals.
+ *
+ * @sync_bit : true : The sync signal keeps only 1bit period.
+ *             false: The sync signal keeps during 8bits period.
+ * @valid_bit: true : The valid signal does not keep during 8bits period.
+ *             false: The valid signal keeps during 8bits period.
+ * @clk_fall : true : Latch the data at falling edge of clock signal.
+ *             false: Latch the data at rising edge of clock signal.
+ */
+static int hsc_css_set_polarity(struct hsc_chip *chip,
+				const struct hsc_reg_css_pol *pol,
+				bool sync_bit, bool val_bit, bool clk_fall)
+{
+	struct regmap *r = chip->regmap;
+	u32 m = 0, v = 0;
+
+	if (!pol->valid)
+		return -EINVAL;
+
+	if (pol->sft_sync != -1) {
+		m |= BIT(pol->sft_sync);
+		if (sync_bit)
+			v |= BIT(pol->sft_sync);
+	}
+
+	if (pol->sft_val != -1) {
+		m |= BIT(pol->sft_val);
+		if (val_bit)
+			v |= BIT(pol->sft_val);
+	}
+
+	if (pol->sft_clk != -1) {
+		m |= BIT(pol->sft_clk);
+		if (clk_fall)
+			v |= BIT(pol->sft_clk);
+	}
+
+	regmap_update_bits(r, pol->reg, m, v);
+
+	return 0;
+}
+
+int hsc_css_in_get_polarity(struct hsc_chip *chip, int in,
+			    bool *sync_bit, bool *val_bit, bool *clk_fall)
+{
+	const struct hsc_spec_css *speci = css_in_get_spec(chip, in);
+
+	if (!speci)
+		return -EINVAL;
+
+	return hsc_css_get_polarity(chip, &speci->pol,
+				    sync_bit, val_bit, clk_fall);
+}
+
+int hsc_css_in_set_polarity(struct hsc_chip *chip, int in,
+			    bool sync_bit, bool val_bit, bool clk_fall)
+{
+	const struct hsc_spec_css *speci = css_in_get_spec(chip, in);
+
+	if (!speci)
+		return -EINVAL;
+
+	return hsc_css_set_polarity(chip, &speci->pol,
+				    sync_bit, val_bit, clk_fall);
+}
+
+int hsc_css_out_get_polarity(struct hsc_chip *chip, int out,
+			     bool *sync_bit, bool *val_bit, bool *clk_fall)
+{
+	const struct hsc_spec_css *speco = css_out_get_spec(chip, out);
+
+	if (!speco)
+		return -EINVAL;
+
+	return hsc_css_get_polarity(chip, &speco->pol,
+				    sync_bit, val_bit, clk_fall);
+}
+
+int hsc_css_out_set_polarity(struct hsc_chip *chip, int out,
+			     bool sync_bit, bool val_bit, bool clk_fall)
+{
+	const struct hsc_spec_css *speco = css_out_get_spec(chip, out);
+
+	if (!speco)
+		return -EINVAL;
+
+	return hsc_css_set_polarity(chip, &speco->pol,
+				    sync_bit, val_bit, clk_fall);
+}
+
+int hsc_css_out_get_src(struct hsc_chip *chip, int *tsi, int out, bool *en)
+{
+	struct regmap *r = chip->regmap;
+	const struct hsc_spec_css *speco = css_out_get_spec(chip, out);
+	u32 v;
+
+	if (!tsi || !en || !speco || !speco->sel.valid)
+		return -EINVAL;
+
+	regmap_read(r, speco->sel.reg, &v);
+	*tsi = (v & speco->sel.mask) >> speco->sel.sft;
+
+	regmap_read(r, CSS_OUTPUTENABLE, &v);
+	*en = !!(v & BIT(out));
+
+	return 0;
+}
+
+/**
+ * Connect the input port and output port using CSS (Cross Stream Switch).
+ *
+ * @in   : Input port number, use one of HSC_CSS_IN_*.
+ * @out  : Output port number, use one of HSC_CSS_OUT_*.
+ * @en   : false: Disable this path.
+ *         true : Enable this path.
+ */
+int hsc_css_out_set_src(struct hsc_chip *chip, int in, int out, bool en)
+{
+	struct regmap *r = chip->regmap;
+	const struct hsc_spec_css *speco = css_out_get_spec(chip, out);
+
+	if (!css_in_is_valid(chip, in) || !speco || !speco->sel.valid)
+		return -EINVAL;
+
+	regmap_update_bits(r, speco->sel.reg, speco->sel.mask,
+			   in << speco->sel.sft);
+
+	regmap_update_bits(r, CSS_OUTPUTENABLE, BIT(out), (en) ? ~0 : 0);
+
+	return 0;
+}
diff --git a/drivers/media/platform/uniphier/hsc-reg.h b/drivers/media/platform/uniphier/hsc-reg.h
index 2d87960c9b97..e5bc87658361 100644
--- a/drivers/media/platform/uniphier/hsc-reg.h
+++ b/drivers/media/platform/uniphier/hsc-reg.h
@@ -115,4 +115,33 @@
 #define CDMBC_CHATPAGED(i, j)        (0x3c10 + ((i) - 1) * 0x48 + (j) * 0x10)
 #define CDMBC_CHATPAGEU(i, j)        (0x3c14 + ((i) - 1) * 0x48 + (j) * 0x10)
 
+/* CSS */
+#define CSS_PTSOCONFIG                   0x1c00
+#define CSS_PTSISIGNALPOL                0x1c04
+#define CSS_SIGNALPOLCH(i)               (0x1c08 + (i) * 0x4)
+#define CSS_OUTPUTENABLE                 0x1c10
+#define CSS_OUTPUTCTRL(i)                (0x1c14 + (i) * 0x4)
+#define CSS_STSOCONFIG                   0x1c2c
+#define CSS_STSOSIGNALPOL                0x1c30
+#define CSS_DMDSIGNALPOL                 0x1c34
+#define CSS_PTSOSIGNALPOL                0x1c38
+#define CSS_PF0CONFIG                    0x1c3c
+#define CSS_PF1CONFIG                    0x1c40
+#define CSS_PFINTENABLE                  0x1c44
+#define CSS_PFINTSTATUS                  0x1c48
+#define CSS_AVOUTPUTCTRL(i)              (0x1c4c + (i) * 0x4)
+#define CSS_DPCTRL(i)                    (0x1c54 + (i) * 0x4)
+#define   CSS_DPCTRL_DPSEL_MASK            GENMASK(22, 0)
+#define   CSS_DPCTRL_DPSEL_PLAY5           BIT(15)
+#define   CSS_DPCTRL_DPSEL_PLAY4           BIT(14)
+#define   CSS_DPCTRL_DPSEL_PLAY3           BIT(13)
+#define   CSS_DPCTRL_DPSEL_PLAY2           BIT(12)
+#define   CSS_DPCTRL_DPSEL_PLAY1           BIT(11)
+#define   CSS_DPCTRL_DPSEL_PLAY0           BIT(10)
+#define   CSS_DPCTRL_DPSEL_TSI4            BIT(4)
+#define   CSS_DPCTRL_DPSEL_TSI3            BIT(3)
+#define   CSS_DPCTRL_DPSEL_TSI2            BIT(2)
+#define   CSS_DPCTRL_DPSEL_TSI1            BIT(1)
+#define   CSS_DPCTRL_DPSEL_TSI0            BIT(0)
+
 #endif /* DVB_UNIPHIER_HSC_REG_H__ */
diff --git a/drivers/media/platform/uniphier/hsc.h b/drivers/media/platform/uniphier/hsc.h
index fddc66df81c7..1cbd17b475bf 100644
--- a/drivers/media/platform/uniphier/hsc.h
+++ b/drivers/media/platform/uniphier/hsc.h
@@ -329,6 +329,23 @@ struct hsc_conf {
 	int dma_out;
 };
 
+/* CSS */
+int hsc_css_out_to_ts_in(int out);
+int hsc_css_out_to_dpll_src(int out);
+
+int hsc_dpll_get_src(struct hsc_chip *chip, int dpll, int *src);
+int hsc_dpll_set_src(struct hsc_chip *chip, int dpll, int src);
+int hsc_css_in_get_polarity(struct hsc_chip *chip, int in,
+			    bool *sync_bit, bool *val_bit, bool *clk_fall);
+int hsc_css_in_set_polarity(struct hsc_chip *chip, int in,
+			    bool sync_bit, bool val_bit, bool clk_fall);
+int hsc_css_out_get_polarity(struct hsc_chip *chip, int out,
+			     bool *sync_bit, bool *val_bit, bool *clk_fall);
+int hsc_css_out_set_polarity(struct hsc_chip *chip, int out,
+			     bool sync_bit, bool val_bit, bool clk_fall);
+int hsc_css_out_get_src(struct hsc_chip *chip, int *in, int out, bool *en);
+int hsc_css_out_set_src(struct hsc_chip *chip, int in, int out, bool en);
+
 /* DMA */
 u64 hsc_rb_cnt(struct hsc_dma_buf *buf);
 u64 hsc_rb_cnt_to_end(struct hsc_dma_buf *buf);
