1351 lines
41 KiB
Diff
1351 lines
41 KiB
Diff
From 88453f29216e4dc473683b0fa9fe5e3e7ae0bff2 Mon Sep 17 00:00:00 2001
|
|
From: Romuald JEANNE <romuald.jeanne@st.com>
|
|
Date: Tue, 16 Mar 2021 09:11:20 +0100
|
|
Subject: [PATCH 10/22] ARM 5.10.10-stm32mp1-r1 MEDIA-SOC-THERMAL
|
|
|
|
Signed-off-by: Romuald JEANNE <romuald.jeanne@st.com>
|
|
---
|
|
drivers/media/cec/platform/Makefile | 1 +
|
|
drivers/media/i2c/ov5640.c | 111 +++++++---
|
|
drivers/media/platform/stm32/stm32-dcmi.c | 122 +++++++++--
|
|
drivers/media/v4l2-core/v4l2-fwnode.c | 3 +
|
|
drivers/soc/Kconfig | 1 +
|
|
drivers/soc/Makefile | 1 +
|
|
drivers/soc/st/Kconfig | 17 ++
|
|
drivers/soc/st/Makefile | 2 +
|
|
drivers/soc/st/stm32_hdp.c | 242 ++++++++++++++++++++++
|
|
drivers/soc/st/stm32_pm_domain.c | 212 +++++++++++++++++++
|
|
drivers/thermal/st/stm_thermal.c | 30 +--
|
|
include/dt-bindings/soc/stm32-hdp.h | 108 ++++++++++
|
|
include/media/v4l2-fwnode.h | 2 +
|
|
13 files changed, 784 insertions(+), 68 deletions(-)
|
|
create mode 100644 drivers/soc/st/Kconfig
|
|
create mode 100644 drivers/soc/st/Makefile
|
|
create mode 100644 drivers/soc/st/stm32_hdp.c
|
|
create mode 100644 drivers/soc/st/stm32_pm_domain.c
|
|
create mode 100644 include/dt-bindings/soc/stm32-hdp.h
|
|
|
|
diff --git a/drivers/media/cec/platform/Makefile b/drivers/media/cec/platform/Makefile
|
|
index 3a947159b25a..ea6f8ee8161c 100644
|
|
--- a/drivers/media/cec/platform/Makefile
|
|
+++ b/drivers/media/cec/platform/Makefile
|
|
@@ -10,5 +10,6 @@ obj-$(CONFIG_CEC_MESON_AO) += meson/
|
|
obj-$(CONFIG_CEC_SAMSUNG_S5P) += s5p/
|
|
obj-$(CONFIG_CEC_SECO) += seco/
|
|
obj-$(CONFIG_CEC_STI) += sti/
|
|
+obj-$(CONFIG_CEC_STM32) += stm32/
|
|
obj-$(CONFIG_CEC_TEGRA) += tegra/
|
|
|
|
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
|
|
index 8f0812e85901..d7d36ad863ea 100644
|
|
--- a/drivers/media/i2c/ov5640.c
|
|
+++ b/drivers/media/i2c/ov5640.c
|
|
@@ -65,6 +65,7 @@
|
|
#define OV5640_REG_TIMING_VTS 0x380e
|
|
#define OV5640_REG_TIMING_TC_REG20 0x3820
|
|
#define OV5640_REG_TIMING_TC_REG21 0x3821
|
|
+#define OV5640_REG_DVP_PCLK_DIVIDER 0x3824
|
|
#define OV5640_REG_AEC_CTRL00 0x3a00
|
|
#define OV5640_REG_AEC_B50_STEP 0x3a08
|
|
#define OV5640_REG_AEC_B60_STEP 0x3a0a
|
|
@@ -98,7 +99,8 @@
|
|
#define OV5640_REG_AVG_READOUT 0x56a1
|
|
|
|
enum ov5640_mode_id {
|
|
- OV5640_MODE_QCIF_176_144 = 0,
|
|
+ OV5640_MODE_QQVGA_160_120 = 0,
|
|
+ OV5640_MODE_QCIF_176_144,
|
|
OV5640_MODE_QVGA_320_240,
|
|
OV5640_MODE_VGA_640_480,
|
|
OV5640_MODE_NTSC_720_480,
|
|
@@ -113,7 +115,6 @@ enum ov5640_mode_id {
|
|
enum ov5640_frame_rate {
|
|
OV5640_15_FPS = 0,
|
|
OV5640_30_FPS,
|
|
- OV5640_60_FPS,
|
|
OV5640_NUM_FRAMERATES,
|
|
};
|
|
|
|
@@ -155,7 +156,6 @@ MODULE_PARM_DESC(virtual_channel,
|
|
static const int ov5640_framerates[] = {
|
|
[OV5640_15_FPS] = 15,
|
|
[OV5640_30_FPS] = 30,
|
|
- [OV5640_60_FPS] = 60,
|
|
};
|
|
|
|
/* regulator supplies */
|
|
@@ -219,6 +219,7 @@ struct ov5640_ctrls {
|
|
struct v4l2_ctrl *test_pattern;
|
|
struct v4l2_ctrl *hflip;
|
|
struct v4l2_ctrl *vflip;
|
|
+ struct v4l2_ctrl *link_freq;
|
|
};
|
|
|
|
struct ov5640_dev {
|
|
@@ -374,8 +375,8 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0},
|
|
+ {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_XGA_1024_768[] = {
|
|
@@ -393,8 +394,7 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_QVGA_320_240[] = {
|
|
@@ -412,8 +412,25 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+};
|
|
+
|
|
+static const struct reg_value ov5640_setting_QQVGA_160_120[] = {
|
|
+ {0x3c07, 0x08, 0, 0},
|
|
+ {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
|
+ {0x3814, 0x31, 0, 0},
|
|
+ {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
|
|
+ {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
|
|
+ {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
|
|
+ {0x3810, 0x00, 0, 0},
|
|
+ {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
|
|
+ {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
|
|
+ {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
|
|
+ {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
|
|
+ {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
+ {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
+ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_QCIF_176_144[] = {
|
|
@@ -431,8 +448,7 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_NTSC_720_480[] = {
|
|
@@ -450,8 +466,7 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_PAL_720_576[] = {
|
|
@@ -469,8 +484,7 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_720P_1280_720[] = {
|
|
@@ -488,8 +502,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = {
|
|
{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
|
|
{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
|
|
- {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
|
@@ -507,8 +520,8 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0},
|
|
+ {0x5001, 0x83, 0, 0},
|
|
{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
|
{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
|
|
@@ -519,7 +532,6 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
|
{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
|
|
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
|
|
{0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
|
|
- {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
|
|
{0x4005, 0x1a, 0, 0},
|
|
};
|
|
|
|
@@ -538,8 +550,8 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
|
|
+ {0x4407, 0x04, 0, 0},
|
|
+ {0x5001, 0x83, 0, 70},
|
|
};
|
|
|
|
/* power-on sensor init reg table */
|
|
@@ -552,6 +564,11 @@ static const struct ov5640_mode_info ov5640_mode_init_data = {
|
|
|
|
static const struct ov5640_mode_info
|
|
ov5640_mode_data[OV5640_NUM_MODES] = {
|
|
+ {OV5640_MODE_QQVGA_160_120, SUBSAMPLING,
|
|
+ 160, 1896, 120, 984,
|
|
+ ov5640_setting_QQVGA_160_120,
|
|
+ ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
|
|
+ OV5640_30_FPS},
|
|
{OV5640_MODE_QCIF_176_144, SUBSAMPLING,
|
|
176, 1896, 144, 984,
|
|
ov5640_setting_QCIF_176_144,
|
|
@@ -566,7 +583,7 @@ ov5640_mode_data[OV5640_NUM_MODES] = {
|
|
640, 1896, 480, 1080,
|
|
ov5640_setting_VGA_640_480,
|
|
ARRAY_SIZE(ov5640_setting_VGA_640_480),
|
|
- OV5640_60_FPS},
|
|
+ OV5640_30_FPS},
|
|
{OV5640_MODE_NTSC_720_480, SUBSAMPLING,
|
|
720, 1896, 480, 984,
|
|
ov5640_setting_NTSC_720_480,
|
|
@@ -1018,9 +1035,38 @@ static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
|
|
|
|
static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
|
|
{
|
|
+ const struct ov5640_mode_info *mode = sensor->current_mode;
|
|
u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
|
|
+ struct i2c_client *client = sensor->i2c_client;
|
|
+ unsigned int pclk_freq, max_pclk_freq;
|
|
+ u8 dvp_pclk_divider;
|
|
int ret;
|
|
|
|
+ /*
|
|
+ * 1280x720 and 1024x768 are reported to use 'SUBSAMPLING' only,
|
|
+ * but they seems to go through the scaler before subsampling.
|
|
+ */
|
|
+ if (mode->dn_mode == SCALING ||
|
|
+ (mode->id == OV5640_MODE_720P_1280_720) ||
|
|
+ (mode->id == OV5640_MODE_XGA_1024_768))
|
|
+ dvp_pclk_divider = 1;
|
|
+ else
|
|
+ dvp_pclk_divider = 2;
|
|
+
|
|
+ ret = ov5640_write_reg(sensor, OV5640_REG_DVP_PCLK_DIVIDER,
|
|
+ dvp_pclk_divider);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ pclk_freq = rate / dvp_pclk_divider;
|
|
+ max_pclk_freq = sensor->ep.bus.parallel.pclk_max_frequency;
|
|
+
|
|
+ /* clip rate according to optional maximum pixel clock limit */
|
|
+ if (max_pclk_freq && (pclk_freq > max_pclk_freq)) {
|
|
+ rate = max_pclk_freq * dvp_pclk_divider;
|
|
+ dev_dbg(&client->dev, "DVP pixel clock too high (%d > %d Hz), reducing rate...\n",
|
|
+ pclk_freq, max_pclk_freq);
|
|
+ }
|
|
+
|
|
ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
|
|
&bit_div, &pclk_div);
|
|
|
|
@@ -1055,6 +1101,7 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
|
|
(ilog2(pclk_div) << 4));
|
|
}
|
|
|
|
+#if 0
|
|
/* set JPEG framing sizes */
|
|
static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
|
|
const struct ov5640_mode_info *mode)
|
|
@@ -1078,19 +1125,20 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
|
|
|
|
return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact);
|
|
}
|
|
+#endif
|
|
|
|
/* download ov5640 settings to sensor through i2c */
|
|
static int ov5640_set_timings(struct ov5640_dev *sensor,
|
|
const struct ov5640_mode_info *mode)
|
|
{
|
|
int ret;
|
|
-
|
|
+#if 0
|
|
if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
|
|
ret = ov5640_set_jpeg_timings(sensor, mode);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
-
|
|
+#endif
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
|
|
if (ret < 0)
|
|
return ret;
|
|
@@ -2172,12 +2220,12 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
|
|
int i;
|
|
|
|
minfps = ov5640_framerates[OV5640_15_FPS];
|
|
- maxfps = ov5640_framerates[OV5640_60_FPS];
|
|
+ maxfps = ov5640_framerates[OV5640_30_FPS];
|
|
|
|
if (fi->numerator == 0) {
|
|
fi->denominator = maxfps;
|
|
fi->numerator = 1;
|
|
- rate = OV5640_60_FPS;
|
|
+ rate = OV5640_30_FPS;
|
|
goto find_mode;
|
|
}
|
|
|
|
@@ -2260,6 +2308,10 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
|
|
return 0;
|
|
}
|
|
|
|
+static const s64 link_freq_menu_items[] = {
|
|
+ 384000000,
|
|
+};
|
|
+
|
|
static int ov5640_set_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *format)
|
|
@@ -2700,6 +2752,8 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
case V4L2_CID_VFLIP:
|
|
ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
|
|
break;
|
|
+ case V4L2_CID_LINK_FREQ:
|
|
+ return 0;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
@@ -2772,6 +2826,9 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
|
|
V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
|
|
V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
|
|
|
|
+ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
|
|
+ 0, 0, link_freq_menu_items);
|
|
+
|
|
if (hdl->error) {
|
|
ret = hdl->error;
|
|
goto free_ctrls;
|
|
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
|
|
index fd1c41cba52f..60ef8a65f16c 100644
|
|
--- a/drivers/media/platform/stm32/stm32-dcmi.c
|
|
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
|
|
@@ -95,6 +95,9 @@ enum state {
|
|
#define MIN_HEIGHT 16U
|
|
#define MAX_HEIGHT 2592U
|
|
|
|
+/* DMA can sustain YUV 720p@15fps max */
|
|
+#define MAX_DMA_BANDWIDTH (1280 * 720 * 2 * 15)
|
|
+
|
|
#define TIMEOUT_MS 1000
|
|
|
|
#define OVERRUN_ERROR_THRESHOLD 3
|
|
@@ -157,6 +160,7 @@ struct stm32_dcmi {
|
|
struct vb2_queue queue;
|
|
|
|
struct v4l2_fwnode_bus_parallel bus;
|
|
+ enum v4l2_mbus_type bus_type;
|
|
struct completion complete;
|
|
struct clk *mclk;
|
|
enum state state;
|
|
@@ -324,7 +328,7 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
|
|
}
|
|
|
|
/*
|
|
- * Avoid call of dmaengine_terminate_all() between
|
|
+ * Avoid call of dmaengine_terminate_sync() between
|
|
* dmaengine_prep_slave_single() and dmaengine_submit()
|
|
* by locking the whole DMA submission sequence
|
|
*/
|
|
@@ -438,7 +442,7 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
|
|
}
|
|
|
|
/* Abort DMA operation */
|
|
- dmaengine_terminate_all(dcmi->dma_chan);
|
|
+ dmaengine_terminate_sync(dcmi->dma_chan);
|
|
|
|
/* Restart capture */
|
|
if (dcmi_restart_capture(dcmi))
|
|
@@ -777,6 +781,23 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
if (dcmi->bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
|
|
val |= CR_PCKPOL;
|
|
|
|
+ /*
|
|
+ * BT656 embedded synchronisation bus mode.
|
|
+ *
|
|
+ * Default SAV/EAV mode is supported here with default codes
|
|
+ * SAV=0xff000080 & EAV=0xff00009d.
|
|
+ * With DCMI this means LSC=SAV=0x80 & LEC=EAV=0x9d.
|
|
+ */
|
|
+ if (dcmi->bus_type == V4L2_MBUS_BT656) {
|
|
+ val |= CR_ESS;
|
|
+
|
|
+ /* Unmask all codes */
|
|
+ reg_write(dcmi->regs, DCMI_ESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */
|
|
+
|
|
+ /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */
|
|
+ reg_write(dcmi->regs, DCMI_ESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */
|
|
+ }
|
|
+
|
|
reg_write(dcmi->regs, DCMI_CR, val);
|
|
|
|
/* Set crop */
|
|
@@ -784,8 +805,31 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
dcmi_set_crop(dcmi);
|
|
|
|
/* Enable jpeg capture */
|
|
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
|
|
- reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */
|
|
+ if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) {
|
|
+ unsigned int rate;
|
|
+ struct v4l2_streamparm p = {
|
|
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE
|
|
+ };
|
|
+ struct v4l2_fract frame_interval = {1, 30};
|
|
+
|
|
+ ret = v4l2_g_parm_cap(dcmi->vdev, dcmi->entity.source, &p);
|
|
+ if (!ret)
|
|
+ frame_interval = p.parm.capture.timeperframe;
|
|
+
|
|
+ rate = dcmi->fmt.fmt.pix.sizeimage *
|
|
+ frame_interval.denominator / frame_interval.numerator;
|
|
+
|
|
+ /*
|
|
+ * If rate exceed DMA capabilities, switch to snapshot mode
|
|
+ * to ensure that current DMA transfer is elapsed before
|
|
+ * capturing a new JPEG.
|
|
+ */
|
|
+ if (rate > MAX_DMA_BANDWIDTH) {
|
|
+ reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */
|
|
+ dev_dbg(dcmi->dev, "Capture rate is too high for continuous mode (%d > %d bytes/s), switch to snapshot mode\n",
|
|
+ rate, MAX_DMA_BANDWIDTH);
|
|
+ }
|
|
+ }
|
|
|
|
/* Enable dcmi */
|
|
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
|
|
@@ -882,7 +926,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
|
|
|
|
/* Stop all pending DMA operations */
|
|
mutex_lock(&dcmi->dma_lock);
|
|
- dmaengine_terminate_all(dcmi->dma_chan);
|
|
+ dmaengine_terminate_sync(dcmi->dma_chan);
|
|
mutex_unlock(&dcmi->dma_lock);
|
|
|
|
pm_runtime_put(dcmi->dev);
|
|
@@ -1067,8 +1111,9 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f)
|
|
if (ret)
|
|
return ret;
|
|
|
|
- /* Disable crop if JPEG is requested */
|
|
- if (pix->pixelformat == V4L2_PIX_FMT_JPEG)
|
|
+ /* Disable crop if JPEG is requested or BT656 bus is selected */
|
|
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG &&
|
|
+ dcmi->bus_type != V4L2_MBUS_BT656)
|
|
dcmi->do_crop = false;
|
|
|
|
/* pix to mbus format */
|
|
@@ -1574,6 +1619,22 @@ static const struct dcmi_format dcmi_formats[] = {
|
|
.fourcc = V4L2_PIX_FMT_JPEG,
|
|
.mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
|
|
.bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
|
+ .bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
|
+ .bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
|
+ .bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
|
+ .bpp = 1,
|
|
},
|
|
};
|
|
|
|
@@ -1592,6 +1653,11 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi)
|
|
if (dcmi_formats[i].mbus_code != mbus_code.code)
|
|
continue;
|
|
|
|
+ /* Exclude JPEG if BT656 bus is selected */
|
|
+ if (dcmi_formats[i].fourcc == V4L2_PIX_FMT_JPEG &&
|
|
+ dcmi->bus_type == V4L2_MBUS_BT656)
|
|
+ continue;
|
|
+
|
|
/* Code supported, have we got this fourcc yet? */
|
|
for (j = 0; j < num_fmts; j++)
|
|
if (sd_fmts[j]->fourcc ==
|
|
@@ -1745,6 +1811,15 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier,
|
|
|
|
dev_dbg(dcmi->dev, "Subdev \"%s\" bound\n", subdev->name);
|
|
|
|
+ ret = video_register_device(dcmi->vdev, VFL_TYPE_VIDEO, -1);
|
|
+ if (ret) {
|
|
+ dev_err(dcmi->dev, "Failed to register video device\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_dbg(dcmi->dev, "Device registered as %s\n",
|
|
+ video_device_node_name(dcmi->vdev));
|
|
+
|
|
/*
|
|
* Link this sub-device to DCMI, it could be
|
|
* a parallel camera sensor or a bridge
|
|
@@ -1757,10 +1832,11 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier,
|
|
&dcmi->vdev->entity, 0,
|
|
MEDIA_LNK_FL_IMMUTABLE |
|
|
MEDIA_LNK_FL_ENABLED);
|
|
- if (ret)
|
|
+ if (ret) {
|
|
dev_err(dcmi->dev, "Failed to create media pad link with subdev \"%s\"\n",
|
|
subdev->name);
|
|
- else
|
|
+ video_unregister_device(dcmi->vdev);
|
|
+ } else
|
|
dev_dbg(dcmi->dev, "DCMI is now linked to \"%s\"\n",
|
|
subdev->name);
|
|
|
|
@@ -1851,7 +1927,9 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
|
|
dcmi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
|
if (IS_ERR(dcmi->rstc)) {
|
|
- dev_err(&pdev->dev, "Could not get reset control\n");
|
|
+ if (PTR_ERR(dcmi->rstc) != -EPROBE_DEFER)
|
|
+ dev_err(&pdev->dev, "Could not get reset control\n");
|
|
+
|
|
return PTR_ERR(dcmi->rstc);
|
|
}
|
|
|
|
@@ -1873,9 +1951,18 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
dev_err(&pdev->dev, "CSI bus not supported\n");
|
|
return -ENODEV;
|
|
}
|
|
+
|
|
+ if (ep.bus_type == V4L2_MBUS_BT656 &&
|
|
+ ep.bus.parallel.bus_width != 8) {
|
|
+ dev_err(&pdev->dev, "BT656 bus conflicts with %u bits bus width (8 bits required)\n",
|
|
+ ep.bus.parallel.bus_width);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
dcmi->bus.flags = ep.bus.parallel.flags;
|
|
dcmi->bus.bus_width = ep.bus.parallel.bus_width;
|
|
dcmi->bus.data_shift = ep.bus.parallel.data_shift;
|
|
+ dcmi->bus_type = ep.bus_type;
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq <= 0)
|
|
@@ -1972,15 +2059,6 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
}
|
|
dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
|
|
|
|
- ret = video_register_device(dcmi->vdev, VFL_TYPE_VIDEO, -1);
|
|
- if (ret) {
|
|
- dev_err(dcmi->dev, "Failed to register video device\n");
|
|
- goto err_media_entity_cleanup;
|
|
- }
|
|
-
|
|
- dev_dbg(dcmi->dev, "Device registered as %s\n",
|
|
- video_device_node_name(dcmi->vdev));
|
|
-
|
|
/* Buffer queue */
|
|
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
|
|
@@ -2001,7 +2079,7 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
|
|
ret = dcmi_graph_init(dcmi);
|
|
if (ret < 0)
|
|
- goto err_media_entity_cleanup;
|
|
+ goto err_vb2_queue_release;
|
|
|
|
/* Reset device */
|
|
ret = reset_control_assert(dcmi->rstc);
|
|
@@ -2027,7 +2105,10 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
return 0;
|
|
|
|
err_cleanup:
|
|
+ v4l2_async_notifier_unregister(&dcmi->notifier);
|
|
v4l2_async_notifier_cleanup(&dcmi->notifier);
|
|
+err_vb2_queue_release:
|
|
+ vb2_queue_release(q);
|
|
err_media_entity_cleanup:
|
|
media_entity_cleanup(&dcmi->vdev->entity);
|
|
err_device_release:
|
|
@@ -2049,6 +2130,7 @@ static int dcmi_remove(struct platform_device *pdev)
|
|
|
|
v4l2_async_notifier_unregister(&dcmi->notifier);
|
|
v4l2_async_notifier_cleanup(&dcmi->notifier);
|
|
+ vb2_queue_release(&dcmi->queue);
|
|
media_entity_cleanup(&dcmi->vdev->entity);
|
|
v4l2_device_unregister(&dcmi->v4l2_dev);
|
|
media_device_cleanup(&dcmi->mdev);
|
|
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
|
|
index dfc53d11053f..7d0e2f5d1700 100644
|
|
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
|
|
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
|
|
@@ -356,6 +356,9 @@ v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode,
|
|
pr_debug("data-enable-active %s\n", v ? "high" : "low");
|
|
}
|
|
|
|
+ if (!fwnode_property_read_u32(fwnode, "pclk-max-frequency", &v))
|
|
+ bus->pclk_max_frequency = v;
|
|
+
|
|
switch (bus_type) {
|
|
default:
|
|
bus->flags = flags;
|
|
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
|
|
index 425ab6f7e375..30afdfbfd9cc 100644
|
|
--- a/drivers/soc/Kconfig
|
|
+++ b/drivers/soc/Kconfig
|
|
@@ -15,6 +15,7 @@ source "drivers/soc/renesas/Kconfig"
|
|
source "drivers/soc/rockchip/Kconfig"
|
|
source "drivers/soc/samsung/Kconfig"
|
|
source "drivers/soc/sifive/Kconfig"
|
|
+source "drivers/soc/st/Kconfig"
|
|
source "drivers/soc/sunxi/Kconfig"
|
|
source "drivers/soc/tegra/Kconfig"
|
|
source "drivers/soc/ti/Kconfig"
|
|
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
|
|
index 36452bed86ef..6b957db9a9bb 100644
|
|
--- a/drivers/soc/Makefile
|
|
+++ b/drivers/soc/Makefile
|
|
@@ -21,6 +21,7 @@ obj-y += renesas/
|
|
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
|
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
|
|
obj-$(CONFIG_SOC_SIFIVE) += sifive/
|
|
+obj-$(CONFIG_ARCH_STM32) += st/
|
|
obj-y += sunxi/
|
|
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
|
obj-y += ti/
|
|
diff --git a/drivers/soc/st/Kconfig b/drivers/soc/st/Kconfig
|
|
new file mode 100644
|
|
index 000000000000..8ab604999db4
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/Kconfig
|
|
@@ -0,0 +1,17 @@
|
|
+if ARCH_STM32
|
|
+
|
|
+config STM32_PM_DOMAINS
|
|
+ bool "STM32 PM domains"
|
|
+ depends on MACH_STM32MP157
|
|
+ select PM_GENERIC_DOMAINS
|
|
+ default y if MACH_STM32MP157
|
|
+
|
|
+config STM32_HDP
|
|
+ bool "STMicroelectronics STM32MP157 Hardware Debug Port (HDP) pin control"
|
|
+ depends on MACH_STM32MP157
|
|
+ default n if MACH_STM32MP157
|
|
+ help
|
|
+ The Hardware Debug Port allows the observation of internal signals. By using multiplexers,
|
|
+ up to 16 signals for each of 8-bit output can be observed.
|
|
+
|
|
+endif # ARCH_STM32
|
|
diff --git a/drivers/soc/st/Makefile b/drivers/soc/st/Makefile
|
|
new file mode 100644
|
|
index 000000000000..85905b7688ed
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/Makefile
|
|
@@ -0,0 +1,2 @@
|
|
+obj-$(CONFIG_STM32_PM_DOMAINS) += stm32_pm_domain.o
|
|
+obj-$(CONFIG_STM32_HDP) += stm32_hdp.o
|
|
diff --git a/drivers/soc/st/stm32_hdp.c b/drivers/soc/st/stm32_hdp.c
|
|
new file mode 100644
|
|
index 000000000000..47687ebd1ffd
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/stm32_hdp.c
|
|
@@ -0,0 +1,242 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Christophe Roullier <christophe.roullier@st.com>
|
|
+ * for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/pinctrl/pinconf.h>
|
|
+#include <linux/pinctrl/pinconf-generic.h>
|
|
+#include <linux/pinctrl/pinctrl.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/suspend.h>
|
|
+
|
|
+#define HDP_CTRL_ENABLE 1
|
|
+#define HDP_CTRL_DISABLE 0
|
|
+
|
|
+enum {
|
|
+ HDP_CTRL = 0,
|
|
+ HDP_MUX = 0x4,
|
|
+ HDP_VAL = 0x10,
|
|
+ HDP_GPOSET = 0x14,
|
|
+ HDP_GPOCLR = 0x18,
|
|
+ HDP_GPOVAL = 0x1c,
|
|
+ HDP_VERR = 0x3f4,
|
|
+ HDP_IPIDR = 0x3f8,
|
|
+ HDP_SIDR = 0x3fc
|
|
+} HDP_register_offsets;
|
|
+
|
|
+struct data_priv {
|
|
+ struct clk *clk;
|
|
+ int clk_is_enabled;
|
|
+ struct dentry *pwr_dentry;
|
|
+ unsigned char __iomem *hdp_membase;
|
|
+ unsigned int hdp_ctrl;
|
|
+ unsigned int hdp_mux;
|
|
+};
|
|
+
|
|
+/* enable/disable */
|
|
+static int stm32_hdp_enable_set(void *data, int val)
|
|
+{
|
|
+ struct data_priv *e = (struct data_priv *)data;
|
|
+
|
|
+ if (!e->clk)
|
|
+ return -EPERM;
|
|
+
|
|
+ if (val == 1) {
|
|
+ if (clk_prepare_enable(e->clk) < 0) {
|
|
+ pr_err("Failed to enable HDP clock\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ e->clk_is_enabled = 1;
|
|
+ } else {
|
|
+ clk_disable_unprepare(e->clk);
|
|
+ e->clk_is_enabled = 0;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_fops_set(void *data, u64 val)
|
|
+{
|
|
+ unsigned char __iomem *addr = (unsigned char __iomem *)data;
|
|
+
|
|
+ writel_relaxed(val, addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_fops_get(void *data, u64 *val)
|
|
+{
|
|
+ unsigned char __iomem *addr = (unsigned char __iomem *)data;
|
|
+
|
|
+ *val = readl_relaxed(addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEFINE_SIMPLE_ATTRIBUTE(stm32_hdp_fops, stm32_hdp_fops_get,
|
|
+ stm32_hdp_fops_set, "0x%llx\n");
|
|
+
|
|
+static int stm32_hdp_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device_node *np = pdev->dev.of_node;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct resource *res;
|
|
+
|
|
+ struct data_priv *data;
|
|
+ struct dentry *r;
|
|
+
|
|
+ int ret;
|
|
+ const __be32 *getmuxing;
|
|
+ u32 muxing, version;
|
|
+
|
|
+ if (!np)
|
|
+ return -ENODEV;
|
|
+
|
|
+ data = devm_kzalloc(&pdev->dev, sizeof(struct data_priv), GFP_KERNEL);
|
|
+ if (!data)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ data->hdp_membase = devm_ioremap_resource(&pdev->dev, res);
|
|
+ if (IS_ERR(data->hdp_membase))
|
|
+ return PTR_ERR(data->hdp_membase);
|
|
+
|
|
+ /* Get HDP clocks */
|
|
+ data->clk = devm_clk_get(dev, "hdp");
|
|
+ if (IS_ERR(data->clk)) {
|
|
+ dev_err(dev, "No HDP CK clock provided...\n");
|
|
+ return PTR_ERR(data->clk);
|
|
+ }
|
|
+
|
|
+ /* Enable clock */
|
|
+ ret = stm32_hdp_enable_set(data, 1);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ getmuxing = of_get_property(np, "muxing-hdp", NULL);
|
|
+ if (!getmuxing) {
|
|
+ dev_err(dev,
|
|
+ "no muxing-hdp property in node\n");
|
|
+ /* Disable clock */
|
|
+ ret = stm32_hdp_enable_set(data, 0);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* add hdp directory */
|
|
+ r = debugfs_create_dir("hdp", NULL);
|
|
+ if (!r) {
|
|
+ dev_err(dev, "Unable to create HDP debugFS\n");
|
|
+ /* Disable clock */
|
|
+ ret = stm32_hdp_enable_set(data, 0);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ debugfs_create_file("ctrl", 0644, r,
|
|
+ data->hdp_membase + HDP_CTRL, &stm32_hdp_fops);
|
|
+ debugfs_create_file("mux", 0644, r,
|
|
+ data->hdp_membase + HDP_MUX, &stm32_hdp_fops);
|
|
+ debugfs_create_file("val", 0644, r,
|
|
+ data->hdp_membase + HDP_VAL, &stm32_hdp_fops);
|
|
+ debugfs_create_file("gposet", 0644, r,
|
|
+ data->hdp_membase + HDP_GPOSET, &stm32_hdp_fops);
|
|
+ debugfs_create_file("gpoclr", 0644, r,
|
|
+ data->hdp_membase + HDP_GPOCLR, &stm32_hdp_fops);
|
|
+ debugfs_create_file("gpoval", 0644, r,
|
|
+ data->hdp_membase + HDP_GPOVAL, &stm32_hdp_fops);
|
|
+
|
|
+ /* Enable HDP */
|
|
+ writel(HDP_CTRL_ENABLE, data->hdp_membase + HDP_CTRL);
|
|
+
|
|
+ /* HDP Multiplexing */
|
|
+ muxing = of_read_number(getmuxing,
|
|
+ of_n_addr_cells(np));
|
|
+
|
|
+ writel(muxing, data->hdp_membase + HDP_MUX);
|
|
+
|
|
+ platform_set_drvdata(pdev, data);
|
|
+
|
|
+ /* Get Majeur, Minor version */
|
|
+ version = readl(data->hdp_membase + HDP_VERR);
|
|
+
|
|
+ dev_info(dev, "STM32 HDP version %d.%d initialized\n",
|
|
+ version >> 4, version & 0x0F);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct data_priv *data = platform_get_drvdata(pdev);
|
|
+
|
|
+ /* Disable HDP */
|
|
+ writel(HDP_CTRL_DISABLE, data->hdp_membase + HDP_CTRL);
|
|
+
|
|
+ if (data->clk) {
|
|
+ if (data->clk_is_enabled)
|
|
+ clk_disable_unprepare(data->clk);
|
|
+ }
|
|
+
|
|
+ pr_info("driver STM32 HDP removed\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
+static int stm32_hdp_suspend(struct device *dev)
|
|
+{
|
|
+ struct data_priv *data = dev_get_drvdata(dev);
|
|
+
|
|
+ data->hdp_ctrl = readl_relaxed(data->hdp_membase + HDP_CTRL);
|
|
+ data->hdp_mux = readl_relaxed(data->hdp_membase + HDP_MUX);
|
|
+
|
|
+ pinctrl_pm_select_sleep_state(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_resume(struct device *dev)
|
|
+{
|
|
+ struct data_priv *data = dev_get_drvdata(dev);
|
|
+
|
|
+ writel_relaxed(data->hdp_ctrl, data->hdp_membase + HDP_CTRL);
|
|
+ writel_relaxed(data->hdp_mux, data->hdp_membase + HDP_MUX);
|
|
+
|
|
+ pinctrl_pm_select_default_state(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif /* CONFIG_PM_SLEEP */
|
|
+
|
|
+static SIMPLE_DEV_PM_OPS(stm32_hdp_pm_ops,
|
|
+ stm32_hdp_suspend,
|
|
+ stm32_hdp_resume);
|
|
+
|
|
+static const struct of_device_id hdp_match[] = {
|
|
+ { .compatible = "st,stm32mp1-hdp",},
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, hdp_match);
|
|
+
|
|
+static struct platform_driver hdp_driver = {
|
|
+ .probe = stm32_hdp_probe,
|
|
+ .remove = stm32_hdp_remove,
|
|
+ .driver = {
|
|
+ .name = "hdp",
|
|
+ .of_match_table = hdp_match,
|
|
+ .pm = &stm32_hdp_pm_ops,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(hdp_driver);
|
|
diff --git a/drivers/soc/st/stm32_pm_domain.c b/drivers/soc/st/stm32_pm_domain.c
|
|
new file mode 100644
|
|
index 000000000000..0386624c20f2
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/stm32_pm_domain.c
|
|
@@ -0,0 +1,212 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com> for STMicroelectronics.
|
|
+ * Author: Olivier Bideau <olivier.bideau@st.com> for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#include <linux/arm-smccc.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/pm_domain.h>
|
|
+#include <linux/printk.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#define SMC(domain, state) \
|
|
+{ \
|
|
+ struct arm_smccc_res res; \
|
|
+ arm_smccc_smc(0x82001008, domain, state, 0, \
|
|
+ 0, 0, 0, 0, &res); \
|
|
+}
|
|
+
|
|
+#define STM32_SMC_PD_DOMAIN_ON 0
|
|
+#define STM32_SMC_PD_DOMAIN_OFF 1
|
|
+
|
|
+struct stm32_pm_domain {
|
|
+ struct device *dev;
|
|
+ struct generic_pm_domain genpd;
|
|
+ int id;
|
|
+};
|
|
+
|
|
+static int stm32_pd_power_off(struct generic_pm_domain *domain)
|
|
+{
|
|
+ struct stm32_pm_domain *priv = container_of(domain,
|
|
+ struct stm32_pm_domain,
|
|
+ genpd);
|
|
+
|
|
+ SMC(priv->id, STM32_SMC_PD_DOMAIN_OFF);
|
|
+
|
|
+ dev_dbg(priv->dev, "%s OFF\n", domain->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_pd_power_on(struct generic_pm_domain *domain)
|
|
+{
|
|
+ struct stm32_pm_domain *priv = container_of(domain,
|
|
+ struct stm32_pm_domain,
|
|
+ genpd);
|
|
+
|
|
+ SMC(priv->id, STM32_SMC_PD_DOMAIN_ON);
|
|
+
|
|
+ dev_dbg(priv->dev, "%s ON\n", domain->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void stm32_pm_domain_remove(struct stm32_pm_domain *domain)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = pm_genpd_remove(&domain->genpd);
|
|
+ if (ret)
|
|
+ dev_err(domain->dev, "failed to remove PM domain %s: %d\n",
|
|
+ domain->genpd.name, ret);
|
|
+}
|
|
+
|
|
+static int stm32_pm_domain_add(struct stm32_pm_domain *domain,
|
|
+ struct device *dev,
|
|
+ struct device_node *np)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ domain->dev = dev;
|
|
+ domain->genpd.name = np->name;
|
|
+ domain->genpd.power_off = stm32_pd_power_off;
|
|
+ domain->genpd.power_on = stm32_pd_power_on;
|
|
+ domain->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
|
|
+
|
|
+ ret = of_property_read_u32(np, "reg", &domain->id);
|
|
+ if (ret) {
|
|
+ dev_err(domain->dev, "no domain ID\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = pm_genpd_init(&domain->genpd, NULL, 0);
|
|
+ if (ret < 0) {
|
|
+ dev_err(domain->dev, "failed to initialise PM domain %s: %d\n",
|
|
+ np->name, ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = of_genpd_add_provider_simple(np, &domain->genpd);
|
|
+ if (ret < 0) {
|
|
+ dev_err(domain->dev, "failed to register PM domain %s: %d\n",
|
|
+ np->name, ret);
|
|
+ stm32_pm_domain_remove(domain);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_info(domain->dev, "domain %s registered\n", np->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void stm32_pm_subdomain_add(struct stm32_pm_domain *domain,
|
|
+ struct device *dev,
|
|
+ struct device_node *np)
|
|
+{
|
|
+ struct device_node *np_child;
|
|
+ int ret;
|
|
+
|
|
+ for_each_child_of_node(np, np_child) {
|
|
+ struct stm32_pm_domain *sub_domain;
|
|
+
|
|
+ sub_domain = devm_kzalloc(dev, sizeof(*sub_domain), GFP_KERNEL);
|
|
+ if (!sub_domain)
|
|
+ continue;
|
|
+
|
|
+ sub_domain->dev = dev;
|
|
+ sub_domain->genpd.name = np_child->name;
|
|
+ sub_domain->genpd.power_off = stm32_pd_power_off;
|
|
+ sub_domain->genpd.power_on = stm32_pd_power_on;
|
|
+ sub_domain->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
|
|
+
|
|
+ ret = of_property_read_u32(np_child, "reg", &sub_domain->id);
|
|
+ if (ret) {
|
|
+ dev_err(sub_domain->dev, "no domain ID\n");
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = pm_genpd_init(&sub_domain->genpd, NULL, 0);
|
|
+ if (ret < 0) {
|
|
+ dev_err(sub_domain->dev, "failed to initialise PM domain %s: %d\n"
|
|
+ , np_child->name, ret);
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = of_genpd_add_provider_simple(np_child,
|
|
+ &sub_domain->genpd);
|
|
+ if (ret < 0) {
|
|
+ dev_err(sub_domain->dev, "failed to register PM domain %s: %d\n"
|
|
+ , np_child->name, ret);
|
|
+ stm32_pm_domain_remove(sub_domain);
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = pm_genpd_add_subdomain(&domain->genpd,
|
|
+ &sub_domain->genpd);
|
|
+
|
|
+ if (ret < 0) {
|
|
+ dev_err(sub_domain->dev, "failed to add Sub PM domain %s: %d\n"
|
|
+ , np_child->name, ret);
|
|
+ stm32_pm_domain_remove(sub_domain);
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ dev_info(sub_domain->dev, "subdomain %s registered\n",
|
|
+ np_child->name);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int stm32_pm_domain_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *np = dev->of_node, *child_np;
|
|
+ int ret;
|
|
+
|
|
+ for_each_child_of_node(np, child_np) {
|
|
+ struct stm32_pm_domain *domain;
|
|
+
|
|
+ domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL);
|
|
+ if (!domain)
|
|
+ continue;
|
|
+
|
|
+ ret = stm32_pm_domain_add(domain, dev, child_np);
|
|
+ if (ret) {
|
|
+ devm_kfree(dev, domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ stm32_pm_subdomain_add(domain, dev, child_np);
|
|
+ }
|
|
+
|
|
+ dev_info(dev, "domains probed\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id stm32_pm_domain_matches[] = {
|
|
+ { .compatible = "st,stm32mp157c-pd", },
|
|
+ { },
|
|
+};
|
|
+
|
|
+static struct platform_driver stm32_pm_domains_driver = {
|
|
+ .probe = stm32_pm_domain_probe,
|
|
+ .driver = {
|
|
+ .name = "stm32-pm-domain",
|
|
+ .of_match_table = stm32_pm_domain_matches,
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init stm32_pm_domains_init(void)
|
|
+{
|
|
+ return platform_driver_register(&stm32_pm_domains_driver);
|
|
+}
|
|
+core_initcall(stm32_pm_domains_init);
|
|
diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c
|
|
index 5fd3fb8912a6..1e065a3323f9 100644
|
|
--- a/drivers/thermal/st/stm_thermal.c
|
|
+++ b/drivers/thermal/st/stm_thermal.c
|
|
@@ -82,8 +82,7 @@
|
|
#define ONE_MHZ 1000000
|
|
#define POLL_TIMEOUT 5000
|
|
#define STARTUP_TIME 40
|
|
-#define TS1_T0_VAL0 30000 /* 30 celsius */
|
|
-#define TS1_T0_VAL1 130000 /* 130 celsius */
|
|
+#define T0 30000 /* 30 celsius */
|
|
#define NO_HW_TRIG 0
|
|
#define SAMPLING_TIME 15
|
|
|
|
@@ -96,7 +95,7 @@ struct stm_thermal_sensor {
|
|
unsigned int high_temp_enabled;
|
|
int irq;
|
|
void __iomem *base;
|
|
- int t0, fmt0, ramp_coeff;
|
|
+ int fmt0, ramp_coeff;
|
|
};
|
|
|
|
static int stm_enable_irq(struct stm_thermal_sensor *sensor)
|
|
@@ -243,14 +242,6 @@ static int stm_thermal_calibration(struct stm_thermal_sensor *sensor)
|
|
/* Fill in DTS structure with factory sensor values */
|
|
static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor)
|
|
{
|
|
- /* Retrieve engineering calibration temperature */
|
|
- sensor->t0 = readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET) &
|
|
- TS1_T0_MASK;
|
|
- if (!sensor->t0)
|
|
- sensor->t0 = TS1_T0_VAL0;
|
|
- else
|
|
- sensor->t0 = TS1_T0_VAL1;
|
|
-
|
|
/* Retrieve fmt0 and put it on Hz */
|
|
sensor->fmt0 = ADJUST * (readl_relaxed(sensor->base +
|
|
DTS_T0VALR1_OFFSET) & TS1_FMT0_MASK);
|
|
@@ -264,8 +255,8 @@ static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor)
|
|
return -EINVAL;
|
|
}
|
|
|
|
- dev_dbg(sensor->dev, "%s: T0 = %doC, FMT0 = %dHz, RAMP_COEFF = %dHz/oC",
|
|
- __func__, sensor->t0, sensor->fmt0, sensor->ramp_coeff);
|
|
+ dev_dbg(sensor->dev, "%s: FMT0 = %dHz, RAMP_COEFF = %dHz/oC",
|
|
+ __func__, sensor->fmt0, sensor->ramp_coeff);
|
|
|
|
return 0;
|
|
}
|
|
@@ -276,8 +267,7 @@ static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor,
|
|
int freqM;
|
|
|
|
/* Figure out the CLK_PTAT frequency for a given temperature */
|
|
- freqM = ((temp - sensor->t0) * sensor->ramp_coeff) / 1000 +
|
|
- sensor->fmt0;
|
|
+ freqM = ((temp - T0) * sensor->ramp_coeff) / 1000 + sensor->fmt0;
|
|
|
|
/* Figure out the threshold sample number */
|
|
*th = clk_get_rate(sensor->clk) * SAMPLING_TIME / freqM;
|
|
@@ -372,7 +362,7 @@ static int stm_thermal_get_temp(void *data, int *temp)
|
|
return -EINVAL;
|
|
|
|
/* Figure out the temperature in mili celsius */
|
|
- *temp = (freqM - sensor->fmt0) * 1000 / sensor->ramp_coeff + sensor->t0;
|
|
+ *temp = (freqM - sensor->fmt0) * 1000 / sensor->ramp_coeff + T0;
|
|
|
|
return 0;
|
|
}
|
|
@@ -515,11 +505,9 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|
sensor->base = base;
|
|
|
|
sensor->clk = devm_clk_get(&pdev->dev, "pclk");
|
|
- if (IS_ERR(sensor->clk)) {
|
|
- dev_err(&pdev->dev, "%s: failed to fetch PCLK clock\n",
|
|
- __func__);
|
|
- return PTR_ERR(sensor->clk);
|
|
- }
|
|
+ if (IS_ERR(sensor->clk))
|
|
+ return dev_err_probe(&pdev->dev, PTR_ERR(sensor->clk),
|
|
+ "Failed to get PCLK clock\n");
|
|
|
|
stm_disable_irq(sensor);
|
|
|
|
diff --git a/include/dt-bindings/soc/stm32-hdp.h b/include/dt-bindings/soc/stm32-hdp.h
|
|
new file mode 100644
|
|
index 000000000000..d98665327281
|
|
--- /dev/null
|
|
+++ b/include/dt-bindings/soc/stm32-hdp.h
|
|
@@ -0,0 +1,108 @@
|
|
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Roullier Christophe <christophe.roullier@st.com>
|
|
+ * for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_STM32_HDP_H
|
|
+#define _DT_BINDINGS_STM32_HDP_H
|
|
+
|
|
+#define STM32_HDP(port, value) ((value) << ((port) * 4))
|
|
+
|
|
+/* define HDP Pins number*/
|
|
+#define HDP0_PWR_PWRWAKE_SYS 0
|
|
+#define HDP0_CM4_SLEEPDEEP 1
|
|
+#define HDP0_PWR_STDBY_WKUP 2
|
|
+#define HDP0_PWR_ENCOMP_VDDCORE 3
|
|
+#define HDP0_BSEC_OUT_SEC_NIDEN 4
|
|
+#define HDP0_RCC_CM4_SLEEPDEEP 6
|
|
+#define HDP0_GPU_DBG7 7
|
|
+#define HDP0_DDRCTRL_LP_REQ 8
|
|
+#define HDP0_PWR_DDR_RET_ENABLE_N 9
|
|
+#define HDP0_GPOVAL_0 15
|
|
+
|
|
+#define HDP1_PWR_PWRWAKE_MCU 0
|
|
+#define HDP1_CM4_HALTED 1
|
|
+#define HDP1_CA7_NAXIERRIRQ 2
|
|
+#define HDP1_PWR_OKIN_MR 3
|
|
+#define HDP1_BSEC_OUT_SEC_DBGEN 4
|
|
+#define HDP1_EXTI_SYS_WAKEUP 5
|
|
+#define HDP1_RCC_PWRDS_MPU 6
|
|
+#define HDP1_GPU_DBG6 7
|
|
+#define HDP1_DDRCTRL_DFI_CTRLUPD_REQ 8
|
|
+#define HDP1_DDRCTRL_CACTIVE_DDRC_ASR 9
|
|
+#define HDP1_GPOVAL_1 15
|
|
+
|
|
+#define HDP2_PWR_PWRWAKE_MPU 0
|
|
+#define HDP2_CM4_RXEV 1
|
|
+#define HDP2_CA7_NPMUIRQ1 2
|
|
+#define HDP2_CA7_NFIQOUT1 3
|
|
+#define HDP2_BSEC_IN_RSTCORE_N 4
|
|
+#define HDP2_EXTI_C2_WAKEUP 5
|
|
+#define HDP2_RCC_PWRDS_MCU 6
|
|
+#define HDP2_GPU_DBG5 7
|
|
+#define HDP2_DDRCTRL_DFI_INIT_COMPLETE 8
|
|
+#define HDP2_DDRCTRL_PERF_OP_IS_REFRESH 9
|
|
+#define HDP2_DDRCTRL_GSKP_DFI_LP_REQ 10
|
|
+#define HDP2_GPOVAL_2 15
|
|
+
|
|
+#define HDP3_PWR_SEL_VTH_VDD_CORE 0
|
|
+#define HDP3_CM4_TXEV 1
|
|
+#define HDP3_CA7_NPMUIRQ0 2
|
|
+#define HDP3_CA7_NFIQOUT0 3
|
|
+#define HDP3_BSEC_OUT_SEC_DFTLOCK 4
|
|
+#define HDP3_EXTI_C1_WAKEUP 5
|
|
+#define HDP3_RCC_PWRDS_SYS 6
|
|
+#define HDP3_GPU_DBG4 7
|
|
+#define HDP3_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE0 8
|
|
+#define HDP3_DDRCTRL_CACTIVE_1 9
|
|
+#define HDP3_GPOVAL_3 15
|
|
+
|
|
+#define HDP4_PWR_PDDS 0
|
|
+#define HDP4_CM4_SLEEPING 1
|
|
+#define HDP4_CA7_NRESET1 2
|
|
+#define HDP4_CA7_NIRQOUT1 3
|
|
+#define HDP4_BSEC_OUT_SEC_DFTEN 4
|
|
+#define HDP4_BSEC_OUT_SEC_DBGSWENABLE 5
|
|
+#define HDP4_ETH_OUT_PMT_INTR_O 6
|
|
+#define HDP4_GPU_DBG3 7
|
|
+#define HDP4_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE1 8
|
|
+#define HDP4_DDRCTRL_CACTIVE_0 9
|
|
+#define HDP4_GPOVAL_4 15
|
|
+
|
|
+#define HDP5_CA7_STANDBYWFIL2 0
|
|
+#define HDP5_PWR_VTH_VDDCORE_ACK 1
|
|
+#define HDP5_CA7_NRESET0 2
|
|
+#define HDP5_CA7_NIRQOUT0 3
|
|
+#define HDP5_BSEC_IN_PWROK 4
|
|
+#define HDP5_BSEC_OUT_SEC_DEVICEEN 5
|
|
+#define HDP5_ETH_OUT_LPI_INTR_O 6
|
|
+#define HDP5_GPU_DBG2 7
|
|
+#define HDP5_DDRCTRL_CACTIVE_DDRC 8
|
|
+#define HDP5_DDRCTRL_WR_CREDIT_CNT 9
|
|
+#define HDP5_GPOVAL_5 15
|
|
+
|
|
+#define HDP6_CA7_STANDBYWFI1 0
|
|
+#define HDP6_CA7_STANDBYWFE1 1
|
|
+#define HDP6_CA7_EVENT0 2
|
|
+#define HDP6_CA7_DBGACK1 3
|
|
+#define HDP6_BSEC_OUT_SEC_SPNIDEN 5
|
|
+#define HDP6_ETH_OUT_MAC_SPEED_O1 6
|
|
+#define HDP6_GPU_DBG1 7
|
|
+#define HDP6_DDRCTRL_CSYSACK_DDRC 8
|
|
+#define HDP6_DDRCTRL_LPR_CREDIT_CNT 9
|
|
+#define HDP6_GPOVAL_6 15
|
|
+
|
|
+#define HDP7_CA7_STANDBYWFI0 0
|
|
+#define HDP7_CA7_STANDBYWFE0 1
|
|
+#define HDP7_CA7_DBGACK0 3
|
|
+#define HDP7_BSEC_OUT_FUSE_OK 4
|
|
+#define HDP7_BSEC_OUT_SEC_SPIDEN 5
|
|
+#define HDP7_ETH_OUT_MAC_SPEED_O0 6
|
|
+#define HDP7_GPU_DBG0 7
|
|
+#define HDP7_DDRCTRL_CSYSREQ_DDRC 8
|
|
+#define HDP7_DDRCTRL_HPR_CREDIT_CNT 9
|
|
+#define HDP7_GPOVAL_7 15
|
|
+
|
|
+#endif /* _DT_BINDINGS_STM32_HDP_H */
|
|
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
|
|
index ed0840f3d5df..598822e23e9e 100644
|
|
--- a/include/media/v4l2-fwnode.h
|
|
+++ b/include/media/v4l2-fwnode.h
|
|
@@ -49,11 +49,13 @@ struct v4l2_fwnode_bus_mipi_csi2 {
|
|
* @flags: media bus (V4L2_MBUS_*) flags
|
|
* @bus_width: bus width in bits
|
|
* @data_shift: data shift in bits
|
|
+ * @max_pclk_frequency: maximum pixel clock in hertz
|
|
*/
|
|
struct v4l2_fwnode_bus_parallel {
|
|
unsigned int flags;
|
|
unsigned char bus_width;
|
|
unsigned char data_shift;
|
|
+ unsigned int pclk_max_frequency;
|
|
};
|
|
|
|
/**
|
|
--
|
|
2.17.1
|
|
|