* [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor
From: Shunqian Zheng @ 2017-12-29 8:08 UTC (permalink / raw)
To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng
This patch adds driver for Omnivision's ov5695 sensor,
the driver supports following features:
- supported resolutions
+ 2592x1944 at 30fps
+ 1920x1080 at 30fps
+ 1296x972 at 60fps
+ 1280x720 at 30fps
+ 640x480 at 120fps
- test patterns
- manual exposure/gain(analog and digital) control
- vblank and hblank
- media controller
- runtime pm
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov5695.c | 1396 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1408 insertions(+)
create mode 100644 drivers/media/i2c/ov5695.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 3c6d642..55b37c8 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -645,6 +645,17 @@ config VIDEO_OV5670
To compile this driver as a module, choose M here: the
module will be called ov5670.
+config VIDEO_OV5695
+ tristate "OmniVision OV5695 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV5695 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov5695.
+
config VIDEO_OV7640
tristate "OmniVision OV7640 sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 548a9ef..a063030 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
+obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
new file mode 100644
index 0000000..4745da4
--- /dev/null
+++ b/drivers/media/i2c/ov5695.c
@@ -0,0 +1,1396 @@
+/*
+ * ov5695 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
+#endif
+
+/* 45Mhz * 4 Binning */
+#define OV5695_PIXEL_RATE (45 * 1000 * 1000 * 4)
+#define CHIP_ID 0x005695
+#define OV5695_REG_CHIP_ID 0x300a
+
+#define OV5695_REG_CTRL_MODE 0x0100
+#define OV5695_MODE_SW_STANDBY 0x0
+#define OV5695_MODE_STREAMING BIT(0)
+
+#define OV5695_REG_EXPOSURE 0x3500
+#define OV5695_EXPOSURE_MIN 4
+#define OV5695_EXPOSURE_STEP 1
+#define OV5695_VTS_MAX 0x7fff
+
+#define OV5695_REG_ANALOG_GAIN 0x3509
+#define ANALOG_GAIN_MIN 0x10
+#define ANALOG_GAIN_MAX 0xf8
+#define ANALOG_GAIN_STEP 1
+#define ANALOG_GAIN_DEFAULT 0xf8
+
+#define OV5695_REG_DIGI_GAIN_H 0x350a
+#define OV5695_REG_DIGI_GAIN_L 0x350b
+#define OV5695_DIGI_GAIN_L_MASK 0x3f
+#define OV5695_DIGI_GAIN_H_SHIFT 6
+#define OV5695_DIGI_GAIN_MIN 0
+#define OV5695_DIGI_GAIN_MAX (0x4000 - 1)
+#define OV5695_DIGI_GAIN_STEP 1
+#define OV5695_DIGI_GAIN_DEFAULT 1024
+
+#define OV5695_REG_TEST_PATTERN 0x4503
+#define OV5695_TEST_PATTERN_ENABLE 0x80
+#define OV5695_TEST_PATTERN_DISABLE 0x0
+
+#define OV5695_REG_VTS 0x380e
+
+#define REG_NULL 0xFFFF
+
+#define OV5695_REG_VALUE_08BIT 1
+#define OV5695_REG_VALUE_16BIT 2
+#define OV5695_REG_VALUE_24BIT 3
+
+#define OV5695_LANES 2
+#define OV5695_BITS_PER_SAMPLE 10
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct ov5695_mode {
+ u32 width;
+ u32 height;
+ u32 max_fps;
+ u32 hts_def;
+ u32 vts_def;
+ u32 exp_def;
+ const struct regval *reg_list;
+};
+
+struct ov5695 {
+ struct i2c_client *client;
+ struct clk *xvclk;
+ struct regulator *avdd_regulator;
+ struct regulator *dovdd_regulator;
+ struct regulator *dvdd_regulator;
+ struct gpio_desc *reset_gpio;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *anal_gain;
+ struct v4l2_ctrl *digi_gain;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *test_pattern;
+ struct mutex mutex;
+ bool streaming;
+ const struct ov5695_mode *cur_mode;
+};
+#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_global_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0300, 0x04},
+ {0x0301, 0x00},
+ {0x0302, 0x69},
+ {0x0303, 0x00},
+ {0x0304, 0x00},
+ {0x0305, 0x01},
+ {0x0307, 0x00},
+ {0x030b, 0x00},
+ {0x030c, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x04},
+ {0x030f, 0x03},
+ {0x0312, 0x01},
+ {0x3000, 0x00},
+ {0x3002, 0xa1},
+ {0x0308, 0x00},
+ {0x0310, 0x00},
+ {0x3022, 0x51},
+ {0x3106, 0x15},
+ {0x3107, 0x01},
+ {0x3108, 0x05},
+ {0x3500, 0x00},
+ {0x3501, 0x45},
+ {0x3502, 0x00},
+ {0x3503, 0x08},
+ {0x3504, 0x03},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0x10},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0x55},
+ {0x3602, 0x58},
+ {0x3614, 0x30},
+ {0x3615, 0x77},
+ {0x3621, 0x08},
+ {0x3624, 0x40},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3638, 0x00},
+ {0x3639, 0x00},
+ {0x363a, 0x00},
+ {0x363b, 0x00},
+ {0x363c, 0xff},
+ {0x363d, 0xfa},
+ {0x3650, 0x44},
+ {0x3651, 0x44},
+ {0x3652, 0x44},
+ {0x3653, 0x44},
+ {0x3654, 0x44},
+ {0x3655, 0x44},
+ {0x3656, 0x44},
+ {0x3657, 0x44},
+ {0x3660, 0x00},
+ {0x3661, 0x00},
+ {0x3662, 0x00},
+ {0x366a, 0x00},
+ {0x366e, 0x0c},
+ {0x3673, 0x04},
+ {0x3700, 0x14},
+ {0x3703, 0x0c},
+ {0x3715, 0x01},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x373f, 0xa0},
+ {0x3765, 0x20},
+ {0x37a1, 0x1d},
+ {0x37a8, 0x26},
+ {0x37ab, 0x14},
+ {0x37c2, 0x04},
+ {0x37cb, 0x09},
+ {0x37cc, 0x13},
+ {0x37cd, 0x1f},
+ {0x37ce, 0x1f},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3810, 0x00},
+ {0x3811, 0x06},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x381a, 0x00},
+ {0x381b, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x3c80, 0x08},
+ {0x3c82, 0x00},
+ {0x3c83, 0x00},
+ {0x3c88, 0x00},
+ {0x3d85, 0x14},
+ {0x3f02, 0x08},
+ {0x3f03, 0x10},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x404e, 0x20},
+ {0x4501, 0x00},
+ {0x4502, 0x10},
+ {0x4800, 0x00},
+ {0x481f, 0x2a},
+ {0x4837, 0x13},
+ {0x5000, 0x17},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5b00, 0x00},
+ {0x5b01, 0x1c},
+ {0x5b02, 0x00},
+ {0x5b03, 0x7f},
+ {0x5b05, 0x6c},
+ {0x5e10, 0xfc},
+ {0x4010, 0xf1},
+ {0x3503, 0x08},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0xf8},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x2e4)
+ * framelength 2024(0x7e8)
+ * grabwindow_width 2592
+ * grabwindow_height 1944
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_2592x1944_regs[] = {
+ {0x3501, 0x7e},
+ {0x366e, 0x18},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xab},
+ {0x3808, 0x0a},
+ {0x3809, 0x20},
+ {0x380a, 0x07},
+ {0x380b, 0x98},
+ {0x380c, 0x02},
+ {0x380d, 0xe4},
+ {0x380e, 0x07},
+ {0x380f, 0xe8},
+ {0x3811, 0x06},
+ {0x3813, 0x08},
+ {0x3814, 0x01},
+ {0x3816, 0x01},
+ {0x3817, 0x01},
+ {0x3820, 0x88},
+ {0x3821, 0x00},
+ {0x4501, 0x00},
+ {0x4008, 0x04},
+ {0x4009, 0x13},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1920
+ * grabwindow_height 1080
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1920x1080_regs[] = {
+ {0x3501, 0x45},
+ {0x366e, 0x18},
+ {0x3800, 0x01},
+ {0x3801, 0x50},
+ {0x3802, 0x01},
+ {0x3803, 0xb8},
+ {0x3804, 0x08},
+ {0x3805, 0xef},
+ {0x3806, 0x05},
+ {0x3807, 0xf7},
+ {0x3808, 0x07},
+ {0x3809, 0x80},
+ {0x380a, 0x04},
+ {0x380b, 0x38},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3811, 0x06},
+ {0x3813, 0x04},
+ {0x3814, 0x01},
+ {0x3816, 0x01},
+ {0x3817, 0x01},
+ {0x3820, 0x88},
+ {0x3821, 0x00},
+ {0x4501, 0x00},
+ {0x4008, 0x04},
+ {0x4009, 0x13},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x02e4)
+ * framelength 1012(0x03f4)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 60fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1296x972_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0300, 0x04},
+ {0x0301, 0x00},
+ {0x0302, 0x69},
+ {0x0303, 0x00},
+ {0x0304, 0x00},
+ {0x0305, 0x01},
+ {0x0307, 0x00},
+ {0x030b, 0x00},
+ {0x030c, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x04},
+ {0x030f, 0x03},
+ {0x0312, 0x01},
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3016, 0x32},
+ {0x3022, 0x51},
+ {0x3106, 0x15},
+ {0x3107, 0x01},
+ {0x3108, 0x05},
+ {0x3500, 0x00},
+ {0x3501, 0x3e},
+ {0x3502, 0x00},
+ {0x3503, 0x08},
+ {0x3504, 0x03},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0x10},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0x55},
+ {0x3602, 0x58},
+ {0x3611, 0x58},
+ {0x3614, 0x30},
+ {0x3615, 0x77},
+ {0x3621, 0x08},
+ {0x3624, 0x40},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3638, 0x00},
+ {0x3639, 0x00},
+ {0x363a, 0x00},
+ {0x363b, 0x00},
+ {0x363c, 0xff},
+ {0x363d, 0xfa},
+ {0x3650, 0x44},
+ {0x3651, 0x44},
+ {0x3652, 0x44},
+ {0x3653, 0x44},
+ {0x3654, 0x44},
+ {0x3655, 0x44},
+ {0x3656, 0x44},
+ {0x3657, 0x44},
+ {0x3660, 0x00},
+ {0x3661, 0x00},
+ {0x3662, 0x00},
+ {0x366a, 0x00},
+ {0x366e, 0x0c},
+ {0x3673, 0x04},
+ {0x3700, 0x14},
+ {0x3703, 0x0c},
+ {0x3706, 0x24},
+ {0x3714, 0x27},
+ {0x3715, 0x01},
+ {0x3716, 0x00},
+ {0x3717, 0x02},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x373f, 0xa0},
+ {0x3765, 0x20},
+ {0x37a1, 0x1d},
+ {0x37a8, 0x26},
+ {0x37ab, 0x14},
+ {0x37c2, 0x04},
+ {0x37c3, 0xf0},
+ {0x37cb, 0x09},
+ {0x37cc, 0x13},
+ {0x37cd, 0x1f},
+ {0x37ce, 0x1f},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+ {0x380c, 0x02},
+ {0x380d, 0xe4},
+ {0x380e, 0x03},
+ {0x380f, 0xf4},
+ {0x3810, 0x00},
+ {0x3811, 0x00},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x381a, 0x00},
+ {0x381b, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x3c80, 0x08},
+ {0x3c82, 0x00},
+ {0x3c83, 0x00},
+ {0x3c88, 0x00},
+ {0x3d85, 0x14},
+ {0x3f02, 0x08},
+ {0x3f03, 0x10},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x404e, 0x20},
+ {0x4501, 0x00},
+ {0x4502, 0x10},
+ {0x4800, 0x00},
+ {0x481f, 0x2a},
+ {0x4837, 0x13},
+ {0x5000, 0x13},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5b00, 0x00},
+ {0x5b01, 0x1c},
+ {0x5b02, 0x00},
+ {0x5b03, 0x7f},
+ {0x5b05, 0x6c},
+ {0x5e10, 0xfc},
+ {0x4010, 0xf1},
+ {0x3503, 0x08},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0xf8},
+ {0x0100, 0x01},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1280
+ * grabwindow_height 720
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1280x720_regs[] = {
+ {0x3501, 0x45},
+ {0x366e, 0x0c},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x01},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x06},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x00},
+ {0x380a, 0x02},
+ {0x380b, 0xd0},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3811, 0x06},
+ {0x3813, 0x02},
+ {0x3814, 0x03},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x4501, 0x00},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 558(0x22e)
+ * grabwindow_width 640
+ * grabwindow_height 480
+ * max_framerate 120fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_640x480_regs[] = {
+ {0x3501, 0x22},
+ {0x366e, 0x0c},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x08},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xa7},
+ {0x3808, 0x02},
+ {0x3809, 0x80},
+ {0x380a, 0x01},
+ {0x380b, 0xe0},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x02},
+ {0x380f, 0x2e},
+ {0x3811, 0x06},
+ {0x3813, 0x04},
+ {0x3814, 0x07},
+ {0x3816, 0x05},
+ {0x3817, 0x03},
+ {0x3820, 0x8d},
+ {0x3821, 0x01},
+ {0x4501, 0x00},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {REG_NULL, 0x00}
+};
+
+static const struct ov5695_mode supported_modes[] = {
+ {
+ .width = 2592,
+ .height = 1944,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02e4 * 4,
+ .vts_def = 0x07e8,
+ .reg_list = ov5695_2592x1944_regs,
+ },
+ {
+ .width = 1920,
+ .height = 1080,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x08b8,
+ .reg_list = ov5695_1920x1080_regs,
+ },
+ {
+ .width = 1296,
+ .height = 972,
+ .max_fps = 60,
+ .exp_def = 0x03e0,
+ .hts_def = 0x02e4 * 4,
+ .vts_def = 0x03f4,
+ .reg_list = ov5695_1296x972_regs,
+ },
+ {
+ .width = 1280,
+ .height = 720,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x08b8,
+ .reg_list = ov5695_1280x720_regs,
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .max_fps = 120,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x022e,
+ .reg_list = ov5695_640x480_regs,
+ },
+};
+
+#define OV5695_LINK_FREQ_420MHZ 420000000
+static const s64 link_freq_menu_items[] = {
+ OV5695_LINK_FREQ_420MHZ
+};
+
+static const char * const ov5695_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+ "Vertical Color Bar Type 2",
+ "Vertical Color Bar Type 3",
+ "Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int ov5695_write_reg(struct i2c_client *client, u16 reg,
+ unsigned int len, u32 val)
+{
+ int buf_i;
+ int val_i;
+ u8 buf[6];
+ u8 *val_p;
+ __be32 val_be;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val_be = cpu_to_be32(val);
+ val_p = (u8 *)&val_be;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int ov5695_write_array(struct i2c_client *client,
+ const struct regval *regs)
+{
+ int i, ret = 0;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+ ret = ov5695_write_reg(client, regs[i].addr,
+ OV5695_REG_VALUE_08BIT, regs[i].val);
+
+ return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+ u32 *val)
+{
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ __be32 data_be = 0;
+ __be16 reg_addr_be = cpu_to_be16(reg);
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)®_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static int ov5695_get_reso_dist(const struct ov5695_mode *mode,
+ struct v4l2_mbus_framefmt *framefmt)
+{
+ return abs(mode->width - framefmt->width) +
+ abs(mode->height - framefmt->height);
+}
+
+static const struct ov5695_mode *ov5695_find_best_fit(
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+ int dist;
+ int cur_best_fit = 0;
+ int cur_best_fit_dist = -1;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+ dist = ov5695_get_reso_dist(&supported_modes[i], framefmt);
+ if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+ cur_best_fit_dist = dist;
+ cur_best_fit = i;
+ }
+ }
+
+ return &supported_modes[cur_best_fit];
+}
+
+static int ov5695_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ const struct ov5695_mode *mode;
+ s64 h_blank, vblank_def;
+
+ mutex_lock(&ov5695->mutex);
+
+ mode = ov5695_find_best_fit(fmt);
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+ mutex_unlock(&ov5695->mutex);
+ return -ENOTTY;
+#endif
+ } else {
+ ov5695->cur_mode = mode;
+ h_blank = mode->hts_def - mode->width;
+ __v4l2_ctrl_modify_range(ov5695->hblank, h_blank,
+ h_blank, 1, h_blank);
+ vblank_def = mode->vts_def - mode->height;
+ __v4l2_ctrl_modify_range(ov5695->vblank, vblank_def,
+ OV5695_VTS_MAX - mode->height,
+ 1, vblank_def);
+ }
+
+ mutex_unlock(&ov5695->mutex);
+ return 0;
+}
+
+static int ov5695_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ const struct ov5695_mode *mode = ov5695->cur_mode;
+
+ mutex_lock(&ov5695->mutex);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+ mutex_unlock(&ov5695->mutex);
+ return -ENOTTY;
+#endif
+ } else {
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.field = V4L2_FIELD_NONE;
+ }
+ mutex_unlock(&ov5695->mutex);
+ return 0;
+}
+
+static int ov5695_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov5695_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index > ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = supported_modes[fse->index].width;
+ fse->max_height = supported_modes[fse->index].height;
+ fse->min_height = supported_modes[fse->index].height;
+
+ return 0;
+}
+
+static int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern)
+{
+ u32 val;
+
+ if (pattern)
+ val = (pattern - 1) | OV5695_TEST_PATTERN_ENABLE;
+ else
+ val = OV5695_TEST_PATTERN_DISABLE;
+
+ return ov5695_write_reg(ov5695->client, OV5695_REG_TEST_PATTERN,
+ OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_exposure(struct ov5695 *ov5695, s32 val)
+{
+ /* 4 least significant bits of expsoure are fractional part */
+ ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE,
+ OV5695_REG_VALUE_24BIT, val << 4);
+}
+
+static void ov5695_set_analog_gain(struct ov5695 *ov5695, s32 val)
+{
+ ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN,
+ OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_digi_gain(struct ov5695 *ov5695, s32 val)
+{
+ ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
+ OV5695_REG_VALUE_08BIT, val & OV5695_DIGI_GAIN_L_MASK);
+ val >>= OV5695_DIGI_GAIN_H_SHIFT;
+ ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
+ OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_vts(struct ov5695 *ov5695, s32 val)
+{
+ val += ov5695->cur_mode->height;
+
+ ov5695_write_reg(ov5695->client, OV5695_REG_VTS,
+ OV5695_REG_VALUE_16BIT, val);
+}
+
+static int __ov5695_start_stream(struct ov5695 *ov5695)
+{
+ int ret;
+
+ ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
+ if (ret)
+ return ret;
+ ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
+ if (ret)
+ return ret;
+
+ /* In case these controls are set before streaming */
+ ov5695_set_exposure(ov5695, ov5695->exposure->val);
+ ov5695_set_analog_gain(ov5695, ov5695->anal_gain->val);
+ ov5695_set_digi_gain(ov5695, ov5695->digi_gain->val);
+ ov5695_set_vts(ov5695, ov5695->vblank->val);
+ ov5695_enable_test_pattern(ov5695, ov5695->test_pattern->val);
+
+ ret = ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+ OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING);
+ return ret;
+}
+
+static int __ov5695_stop_stream(struct ov5695 *ov5695)
+{
+ return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+ OV5695_REG_VALUE_08BIT, OV5695_MODE_SW_STANDBY);
+}
+
+static int ov5695_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ struct i2c_client *client = ov5695->client;
+ int ret = 0;
+
+ mutex_lock(&ov5695->mutex);
+ on = !!on;
+ if (on == ov5695->streaming)
+ goto unlock_and_return;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret)
+ goto unlock_and_return;
+
+ ret = __ov5695_start_stream(ov5695);
+ if (ret) {
+ v4l2_err(sd, "start stream failed while write regs\n");
+ pm_runtime_put(&client->dev);
+ }
+ } else {
+ ret = __ov5695_stop_stream(ov5695);
+ if (ret)
+ goto unlock_and_return;
+ pm_runtime_put(&client->dev);
+ }
+
+ ov5695->streaming = on;
+
+unlock_and_return:
+ mutex_unlock(&ov5695->mutex);
+ return ret;
+}
+
+static int __ov5695_power_on(struct ov5695 *ov5695)
+{
+ int ret;
+ struct device *dev = &ov5695->client->dev;
+
+ ret = clk_prepare_enable(ov5695->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+
+ /* AVDD and DOVDD may rise in any order */
+ ret = regulator_enable(ov5695->avdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulator\n");
+ goto disable_clk;
+ }
+ ret = regulator_enable(ov5695->dovdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable DOVDD regulator\n");
+ goto disable_avdd;
+ }
+ /* DVDD must rise after AVDD and DOVDD */
+ ret = regulator_enable(ov5695->dvdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable DVDD regulator\n");
+ goto disable_dovdd;
+ }
+
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
+ /* 8192 cycles prior to first SCCB transaction */
+ usleep_range(1000, 1500);
+
+ return 0;
+
+disable_dovdd:
+ regulator_disable(ov5695->dovdd_regulator);
+disable_avdd:
+ regulator_disable(ov5695->avdd_regulator);
+disable_clk:
+ clk_disable_unprepare(ov5695->xvclk);
+
+ return ret;
+}
+
+static void __ov5695_power_off(struct ov5695 *ov5695)
+{
+ clk_disable_unprepare(ov5695->xvclk);
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+ regulator_disable(ov5695->dvdd_regulator);
+ regulator_disable(ov5695->dovdd_regulator);
+ regulator_disable(ov5695->avdd_regulator);
+}
+
+static int ov5695_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ int ret;
+
+ ret = __ov5695_power_on(ov5695);
+ if (ret)
+ return ret;
+
+ if (ov5695->streaming) {
+ ret = __ov5695_start_stream(ov5695);
+ if (ret)
+ __ov5695_power_off(ov5695);
+ }
+
+ return ret;
+}
+
+static int ov5695_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5695 *ov5695 = to_ov5695(sd);
+
+ __ov5695_power_off(ov5695);
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+ mutex_lock(&ov5695->mutex);
+ /* Initialize try_fmt */
+ try_fmt->width = ov5695->cur_mode->width;
+ try_fmt->height = ov5695->cur_mode->height;
+ try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ try_fmt->field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&ov5695->mutex);
+ /* No crop or compose */
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops ov5695_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov5695_runtime_suspend,
+ ov5695_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov5695_internal_ops = {
+ .open = ov5695_open,
+};
+#endif
+
+static struct v4l2_subdev_video_ops ov5695_video_ops = {
+ .s_stream = ov5695_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5695_pad_ops = {
+ .enum_mbus_code = ov5695_enum_mbus_code,
+ .enum_frame_size = ov5695_enum_frame_sizes,
+ .get_fmt = ov5695_get_fmt,
+ .set_fmt = ov5695_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov5695_subdev_ops = {
+ .video = &ov5695_video_ops,
+ .pad = &ov5695_pad_ops,
+};
+
+static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5695 *ov5695 = container_of(ctrl->handler,
+ struct ov5695, ctrl_handler);
+ struct i2c_client *client = ov5695->client;
+ s64 max;
+ int ret = 0;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max = ov5695->cur_mode->height + ctrl->val - 4;
+ __v4l2_ctrl_modify_range(ov5695->exposure,
+ ov5695->exposure->minimum, max,
+ ov5695->exposure->step,
+ ov5695->exposure->default_value);
+ break;
+ }
+
+ pm_runtime_get_sync(&client->dev);
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ov5695_set_exposure(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ov5695_set_analog_gain(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ov5695_set_digi_gain(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ ov5695_set_vts(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov5695_enable_test_pattern(ov5695, ctrl->val);
+ break;
+ default:
+ dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ __func__, ctrl->id, ctrl->val);
+ break;
+ };
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5695_ctrl_ops = {
+ .s_ctrl = ov5695_set_ctrl,
+};
+
+static int ov5695_initialize_controls(struct ov5695 *ov5695)
+{
+ const struct ov5695_mode *mode;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl *ctrl;
+ s64 exposure_max, vblank_def;
+ u32 h_blank;
+ int ret;
+
+ handler = &ov5695->ctrl_handler;
+ mode = ov5695->cur_mode;
+ ret = v4l2_ctrl_handler_init(handler, 1);
+ if (ret)
+ return ret;
+ handler->lock = &ov5695->mutex;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, OV5695_PIXEL_RATE, 1, OV5695_PIXEL_RATE);
+
+ h_blank = mode->hts_def - mode->width;
+ ov5695->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov5695->hblank)
+ ov5695->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts_def - mode->height;
+ ov5695->vblank = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_def,
+ OV5695_VTS_MAX - mode->height,
+ 1, vblank_def);
+
+ exposure_max = mode->vts_def - 4;
+ ov5695->exposure = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_EXPOSURE, OV5695_EXPOSURE_MIN,
+ exposure_max, OV5695_EXPOSURE_STEP,
+ mode->exp_def);
+
+ ov5695->anal_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN,
+ ANALOG_GAIN_MAX, ANALOG_GAIN_STEP,
+ ANALOG_GAIN_DEFAULT);
+
+ /* Digital gain */
+ ov5695->digi_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN, OV5695_DIGI_GAIN_MIN,
+ OV5695_DIGI_GAIN_MAX, OV5695_DIGI_GAIN_STEP,
+ OV5695_DIGI_GAIN_DEFAULT);
+
+ ov5695->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+ &ov5695_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov5695_test_pattern_menu) - 1,
+ 0, 0, ov5695_test_pattern_menu);
+
+ if (handler->error) {
+ v4l2_ctrl_handler_free(handler);
+ return handler->error;
+ }
+
+ ov5695->subdev.ctrl_handler = handler;
+
+ return 0;
+}
+
+static int ov5695_check_sensor_id(struct ov5695 *ov5695,
+ struct i2c_client *client)
+{
+ struct device *dev = &ov5695->client->dev;
+ u32 id;
+ int ret;
+
+ ret = __ov5695_power_on(ov5695);
+ if (ret)
+ return ret;
+ ov5695_read_reg(client, OV5695_REG_CHIP_ID,
+ OV5695_REG_VALUE_24BIT, &id);
+ __ov5695_power_off(ov5695);
+
+ if (id != CHIP_ID) {
+ dev_err(dev, "Wrong camera sensor id(%06x)\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+ return 0;
+}
+
+static int ov5695_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ov5695 *ov5695;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
+ if (!ov5695)
+ return -ENOMEM;
+
+ ov5695->client = client;
+ ov5695->cur_mode = &supported_modes[0];
+
+ ov5695->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov5695->xvclk)) {
+ dev_err(dev, "Failed to get xvclk\n");
+ return -EINVAL;
+ }
+ ret = clk_set_rate(ov5695->xvclk, 24000000);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set xvclk rate (24M)\n");
+ return ret;
+ }
+
+ ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov5695->reset_gpio)) {
+ dev_err(dev, "Failed to get reset-gpios\n");
+ return -EINVAL;
+ }
+
+ ov5695->avdd_regulator = devm_regulator_get(dev, "avdd");
+ if (IS_ERR(ov5695->avdd_regulator)) {
+ dev_err(dev, "Failed to get avdd-supply\n");
+ return -EINVAL;
+ }
+ ov5695->dovdd_regulator = devm_regulator_get(dev, "dovdd");
+ if (IS_ERR(ov5695->dovdd_regulator)) {
+ dev_err(dev, "Failed to get dovdd-supply\n");
+ return -EINVAL;
+ }
+ ov5695->dvdd_regulator = devm_regulator_get(dev, "dvdd");
+ if (IS_ERR(ov5695->dvdd_regulator)) {
+ dev_err(dev, "Failed to get dvdd-supply\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&ov5695->mutex);
+
+ sd = &ov5695->subdev;
+ v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops);
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ sd->internal_ops = &ov5695_internal_ops;
+#endif
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ ret = ov5695_initialize_controls(ov5695);
+ if (ret)
+ return ret;
+ ret = ov5695_check_sensor_id(ov5695, client);
+ if (ret)
+ return ret;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad);
+ if (ret < 0)
+ return ret;
+#endif
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto clean_entity;
+ }
+
+ pm_runtime_enable(dev);
+ dev_info(dev, "Probe successfully\n");
+
+ return 0;
+
+clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&sd->entity);
+#endif
+
+ return ret;
+}
+
+static int ov5695_remove(struct i2c_client *client)
+{
+ struct ov5695 *ov5695 = i2c_get_clientdata(client);
+
+ v4l2_async_unregister_subdev(&ov5695->subdev);
+
+ pm_runtime_disable(&client->dev);
+ return 0;
+}
+
+static const struct of_device_id ov5695_of_match[] = {
+ { .compatible = "ovti,ov5695" },
+ {},
+};
+
+static struct i2c_driver ov5695_i2c_driver = {
+ .driver = {
+ .name = "ov5695",
+ .owner = THIS_MODULE,
+ .pm = &ov5695_pm_ops,
+ .of_match_table = ov5695_of_match
+ },
+ .probe = &ov5695_probe,
+ .remove = &ov5695_remove,
+};
+
+module_i2c_driver(ov5695_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 2/4] [media] dt/bindings: Add bindings for OV5695
From: Shunqian Zheng @ 2017-12-29 8:08 UTC (permalink / raw)
To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng
In-Reply-To: <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Add device tree binding documentation for the OV5695 sensor.
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
.../devicetree/bindings/media/i2c/ov5695.txt | 38 ++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5695.txt
diff --git a/Documentation/devicetree/bindings/media/i2c/ov5695.txt b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
new file mode 100644
index 0000000..8d87dbc
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
@@ -0,0 +1,38 @@
+* Omnivision OV5695 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: should be "ovti,ov5695"
+- clocks: reference to the 24M xvclk input clock.
+- clock-names: should be "xvclk".
+- dovdd-supply: Digital I/O voltage supply, 1.8 volts
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dvdd-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: Low active reset gpio
+
+The device node must contain one 'port' child node for its digital output
+video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+&i2c1: camera-sensor@36 {
+ compatible = "ovti,ov5695";
+ reg = <0x36>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&clk_24m_cam>;
+
+ clocks = <&cru SCLK_TESTCLKOUT1>;
+ clock-names = "xvclk";
+
+ avdd-supply = <&pp2800_cam>;
+ dvdd-supply = <&pp1250_cam>;
+ dovdd-supply = <&pp1800>;
+
+ reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
+
+ port {
+ wcam_out: endpoint {
+ remote-endpoint = <&mipi_in_wcam>;
+ data-lanes = <1 2>;
+ };
+ };
+};
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor
From: Shunqian Zheng @ 2017-12-29 8:08 UTC (permalink / raw)
To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng
In-Reply-To: <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
This patch adds driver for Omnivision's ov2685 sensor.
Though the ov2685 can output yuv data, this driver only
supports the raw bayer format, including the following features:
- output 1600x1200 at 30fps
- test patterns
- manual exposure/gain control
- vblank and hblank
- media controller
- runtime pm
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
drivers/media/i2c/Kconfig | 12 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov2685.c | 841 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 854 insertions(+)
create mode 100644 drivers/media/i2c/ov2685.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 55b37c8..63a175d 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -586,6 +586,18 @@ config VIDEO_OV2659
To compile this driver as a module, choose M here: the
module will be called ov2659.
+config VIDEO_OV2685
+ tristate "OmniVision OV2685 sensor support"
+ depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV2685 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov2685.
+
config VIDEO_OV5640
tristate "OmniVision OV5640 sensor support"
depends on OF
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index a063030..3054c69 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
+obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
new file mode 100644
index 0000000..e037d20
--- /dev/null
+++ b/drivers/media/i2c/ov2685.c
@@ -0,0 +1,841 @@
+/*
+ * ov2685 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define CHIP_ID 0x2685
+#define OV2685_REG_CHIP_ID 0x300a
+
+#define REG_SC_CTRL_MODE 0x0100
+#define SC_CTRL_MODE_STANDBY 0x0
+#define SC_CTRL_MODE_STREAMING BIT(0)
+
+#define OV2685_REG_EXPOSURE 0x3500
+#define OV2685_EXPOSURE_MIN 4
+#define OV2685_EXPOSURE_STEP 1
+
+#define OV2685_REG_VTS 0x380e
+#define OV2685_VTS_MAX 0x7fff
+
+#define OV2685_REG_GAIN 0x350a
+#define OV2685_GAIN_MIN 0
+#define OV2685_GAIN_MAX 0x07ff
+#define OV2685_GAIN_STEP 0x1
+#define OV2685_GAIN_DEFAULT 0x0036
+
+#define OV2685_REG_TEST_PATTERN 0x5080
+#define OV2685_TEST_PATTERN_DISABLED 0x00
+#define OV2685_TEST_PATTERN_COLOR_BAR 0x80
+#define OV2685_TEST_PATTERN_RND 0x81
+#define OV2685_TEST_PATTERN_COLOR_BAR_FADE 0x88
+#define OV2685_TEST_PATTERN_BW_SQUARE 0x92
+#define OV2685_TEST_PATTERN_COLOR_SQUARE 0x82
+
+#define REG_NULL 0xFFFF
+
+#define OV2685_REG_VALUE_08BIT 1
+#define OV2685_REG_VALUE_16BIT 2
+#define OV2685_REG_VALUE_24BIT 3
+
+#define OV2685_LANES 1
+#define OV2685_BITS_PER_SAMPLE 10
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct ov2685_mode {
+ u32 width;
+ u32 height;
+ u32 exp_def;
+ u32 hts_def;
+ u32 vts_def;
+ const struct regval *reg_list;
+};
+
+struct ov2685 {
+ struct i2c_client *client;
+ struct clk *xvclk;
+ struct regulator *avdd_regulator; /* Analog power */
+ struct regulator *dovdd_regulator; /* Digital I/O power */
+ /* use internal DVDD power */
+ struct gpio_desc *reset_gpio;
+
+ bool streaming;
+ struct mutex mutex;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_ctrl *anal_gain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *test_pattern;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ const struct ov2685_mode *cur_mode;
+};
+#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
+
+/* PLL settings bases on 24M xvclk */
+static struct regval ov2685_1600x1200_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x3002, 0x00},
+ {0x3016, 0x1c},
+ {0x3018, 0x44},
+ {0x301d, 0xf0},
+ {0x3020, 0x00},
+ {0x3082, 0x37},
+ {0x3083, 0x03},
+ {0x3084, 0x09},
+ {0x3085, 0x04},
+ {0x3086, 0x00},
+ {0x3087, 0x00},
+ {0x3501, 0x4e},
+ {0x3502, 0xe0},
+ {0x3503, 0x07},
+ {0x350b, 0x36},
+ {0x3600, 0xb4},
+ {0x3603, 0x35},
+ {0x3604, 0x24},
+ {0x3605, 0x00},
+ {0x3620, 0x24},
+ {0x3621, 0x34},
+ {0x3622, 0x03},
+ {0x3628, 0x10},
+ {0x3705, 0x3c},
+ {0x370a, 0x21},
+ {0x370c, 0x50},
+ {0x370d, 0xc0},
+ {0x3717, 0x58},
+ {0x3718, 0x80},
+ {0x3720, 0x00},
+ {0x3721, 0x09},
+ {0x3722, 0x06},
+ {0x3723, 0x59},
+ {0x3738, 0x99},
+ {0x3781, 0x80},
+ {0x3784, 0x0c},
+ {0x3789, 0x60},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x06},
+ {0x3805, 0x4f},
+ {0x3806, 0x04},
+ {0x3807, 0xbf},
+ {0x3808, 0x06},
+ {0x3809, 0x40},
+ {0x380a, 0x04},
+ {0x380b, 0xb0},
+ {0x380c, 0x06},
+ {0x380d, 0xa4},
+ {0x380e, 0x05},
+ {0x380f, 0x0e},
+ {0x3810, 0x00},
+ {0x3811, 0x08},
+ {0x3812, 0x00},
+ {0x3813, 0x08},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3819, 0x04},
+ {0x3820, 0xc0},
+ {0x3821, 0x00},
+ {0x3a06, 0x01},
+ {0x3a07, 0x84},
+ {0x3a08, 0x01},
+ {0x3a09, 0x43},
+ {0x3a0a, 0x24},
+ {0x3a0b, 0x60},
+ {0x3a0c, 0x28},
+ {0x3a0d, 0x60},
+ {0x3a0e, 0x04},
+ {0x3a0f, 0x8c},
+ {0x3a10, 0x05},
+ {0x3a11, 0x0c},
+ {0x4000, 0x81},
+ {0x4001, 0x40},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x4300, 0x00},
+ {0x430e, 0x00},
+ {0x4602, 0x02},
+ {0x481b, 0x40},
+ {0x481f, 0x40},
+ {0x4837, 0x18},
+ {0x5000, 0x1f},
+ {0x5001, 0x05},
+ {0x5002, 0x30},
+ {0x5003, 0x04},
+ {0x5004, 0x00},
+ {0x5005, 0x0c},
+ {0x5280, 0x15},
+ {0x5281, 0x06},
+ {0x5282, 0x06},
+ {0x5283, 0x08},
+ {0x5284, 0x1c},
+ {0x5285, 0x1c},
+ {0x5286, 0x20},
+ {0x5287, 0x10},
+ {REG_NULL, 0x00}
+};
+
+#define OV2685_LINK_FREQ_330MHZ 330000000
+static const s64 link_freq_menu_items[] = {
+ OV2685_LINK_FREQ_330MHZ
+};
+
+static const char * const ov2685_test_pattern_menu[] = {
+ "Disabled",
+ "Color Bar",
+ "RND PATTERN",
+ "Color Bar FADE",
+ "BW SQUARE",
+ "COLOR SQUARE"
+};
+
+static const int ov2685_test_pattern_val[] = {
+ OV2685_TEST_PATTERN_DISABLED,
+ OV2685_TEST_PATTERN_COLOR_BAR,
+ OV2685_TEST_PATTERN_RND,
+ OV2685_TEST_PATTERN_COLOR_BAR_FADE,
+ OV2685_TEST_PATTERN_BW_SQUARE,
+ OV2685_TEST_PATTERN_COLOR_SQUARE,
+};
+
+static const struct ov2685_mode supported_modes[] = {
+ {
+ .width = 1600,
+ .height = 1200,
+ .exp_def = 0x04ee,
+ .hts_def = 0x06a4,
+ .vts_def = 0x050e,
+ .reg_list = ov2685_1600x1200_regs,
+ },
+};
+
+/* Write registers up to 4 at a time */
+static int ov2685_write_reg(struct i2c_client *client, u16 reg,
+ unsigned int len, u32 val)
+{
+ int buf_i;
+ int val_i;
+ u8 buf[6];
+ u8 *val_p;
+ __be32 val_be;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val_be = cpu_to_be32(val);
+ val_p = (u8 *)&val_be;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int ov2685_write_array(struct i2c_client *client,
+ const struct regval *regs)
+{
+ int i, ret = 0;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+ ret = ov2685_write_reg(client, regs[i].addr,
+ OV2685_REG_VALUE_08BIT, regs[i].val);
+
+ return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov2685_read_reg(struct i2c_client *client, u16 reg,
+ unsigned int len, u32 *val)
+{
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ __be32 data_be = 0;
+ __be16 reg_addr_be = cpu_to_be16(reg);
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)®_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static void ov2685_fill_fmt(struct ov2685 *ov2685,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->width = ov2685->cur_mode->width;
+ fmt->height = ov2685->cur_mode->height;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov2685_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+ ov2685_fill_fmt(ov2685, mbus_fmt);
+
+ return 0;
+}
+
+static int ov2685_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+ ov2685_fill_fmt(ov2685, mbus_fmt);
+
+ return 0;
+}
+
+static int ov2685_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ return 0;
+}
+
+static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int index = fse->index;
+
+ if (index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ fse->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ fse->min_width = supported_modes[index].width;
+ fse->max_width = supported_modes[index].width;
+ fse->max_height = supported_modes[index].height;
+ fse->min_height = supported_modes[index].height;
+
+ return 0;
+}
+
+static inline void ov2685_set_exposure(struct ov2685 *ov2685, s32 val)
+{
+ ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
+ OV2685_REG_VALUE_24BIT, val << 4);
+}
+
+static inline void ov2685_set_gain(struct ov2685 *ov2685, s32 val)
+{
+ ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
+ OV2685_REG_VALUE_16BIT, val & OV2685_GAIN_MAX);
+}
+
+static inline void ov2685_set_vts(struct ov2685 *ov2685, s32 val)
+{
+ val += ov2685->cur_mode->height;
+ ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
+ OV2685_REG_VALUE_16BIT, val);
+}
+
+static inline void ov2685_enable_test_pattern(struct ov2685 *ov2685, u32 pat)
+{
+ ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
+ OV2685_REG_VALUE_08BIT, ov2685_test_pattern_val[pat]);
+}
+
+static int __ov2685_power_on(struct ov2685 *ov2685)
+{
+ int ret;
+ struct device *dev = &ov2685->client->dev;
+
+ ret = clk_prepare_enable(ov2685->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+ clk_set_rate(ov2685->xvclk, 24000000);
+
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+ /* AVDD and DOVDD may rise in any order */
+ ret = regulator_enable(ov2685->avdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable AVDD regulator\n");
+ goto disable_xvclk;
+ }
+ ret = regulator_enable(ov2685->dovdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable DOVDD regulator\n");
+ goto disable_avdd;
+ }
+ /* The minimum delay between AVDD and reset rising can be 0 */
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
+ /* 8192 xvclk cycles prior to the first SCCB transaction.
+ * NOTE: An additional 1ms must be added to wait for
+ * SCCB to become stable when using internal DVDD.
+ */
+ usleep_range(1350, 1500);
+
+ /* HACK: ov2685 would output messy data after reset(R0103),
+ * writing register before .s_stream() as a workaround
+ */
+ ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
+
+ return ret;
+disable_avdd:
+ regulator_disable(ov2685->avdd_regulator);
+disable_xvclk:
+ clk_disable_unprepare(ov2685->xvclk);
+
+ return ret;
+}
+
+static void __ov2685_power_off(struct ov2685 *ov2685)
+{
+ /* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */
+ usleep_range(30, 50);
+ clk_disable_unprepare(ov2685->xvclk);
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+ regulator_disable(ov2685->dovdd_regulator);
+ regulator_disable(ov2685->avdd_regulator);
+}
+
+static int ov2685_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ int ret = 0;
+
+ mutex_lock(&ov2685->mutex);
+
+ if (on)
+ ret = pm_runtime_get_sync(&ov2685->client->dev);
+ else
+ ret = pm_runtime_put(&ov2685->client->dev);
+
+ mutex_unlock(&ov2685->mutex);
+
+ return ret;
+}
+
+static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct i2c_client *client = ov2685->client;
+ int ret = 0;
+
+ mutex_lock(&ov2685->mutex);
+
+ on = !!on;
+ if (on == ov2685->streaming)
+ goto unlock_and_return;
+
+ if (on) {
+ /* In case these controls are set before streaming */
+ ov2685_set_exposure(ov2685, ov2685->exposure->val);
+ ov2685_set_gain(ov2685, ov2685->anal_gain->val);
+ ov2685_set_vts(ov2685, ov2685->vblank->val);
+ ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);
+
+ ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
+ OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
+ if (ret)
+ goto unlock_and_return;
+ } else {
+ ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
+ OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
+ if (ret)
+ goto unlock_and_return;
+ }
+
+ ov2685->streaming = on;
+
+unlock_and_return:
+ mutex_unlock(&ov2685->mutex);
+ return ret;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *try_fmt;
+
+ mutex_lock(&ov2685->mutex);
+
+ try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+ /* Initialize try_fmt */
+ ov2685_fill_fmt(ov2685, try_fmt);
+
+ mutex_unlock(&ov2685->mutex);
+
+ return 0;
+}
+#endif
+
+static int ov2685_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ int ret;
+
+ ret = __ov2685_power_on(ov2685);
+ if (ret)
+ return ret;
+
+ if (ov2685->streaming) {
+ ret = ov2685_s_stream(sd, 1);
+ if (ret)
+ __ov2685_power_off(ov2685);
+ }
+
+ return ret;
+}
+
+static int ov2685_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2685 *ov2685 = to_ov2685(sd);
+
+ __ov2685_power_off(ov2685);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov2685_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov2685_runtime_suspend,
+ ov2685_runtime_resume, NULL)
+};
+
+static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov2685 *ov2685 = container_of(ctrl->handler,
+ struct ov2685, ctrl_handler);
+ struct i2c_client *client = ov2685->client;
+ s64 max;
+ int ret = 0;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max = ov2685->cur_mode->height + ctrl->val - 4;
+ __v4l2_ctrl_modify_range(ov2685->exposure,
+ ov2685->exposure->minimum, max,
+ ov2685->exposure->step,
+ ov2685->exposure->default_value);
+ break;
+ }
+
+ pm_runtime_get_sync(&client->dev);
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ov2685_set_exposure(ov2685, ctrl->val);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ov2685_set_gain(ov2685, ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ ov2685_set_vts(ov2685, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ov2685_enable_test_pattern(ov2685, ctrl->val);
+ break;
+ default:
+ dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ __func__, ctrl->id, ctrl->val);
+ break;
+ };
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops ov2685_core_ops = {
+ .s_power = ov2685_s_power,
+};
+
+static struct v4l2_subdev_video_ops ov2685_video_ops = {
+ .s_stream = ov2685_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov2685_pad_ops = {
+ .enum_mbus_code = ov2685_enum_mbus_code,
+ .enum_frame_size = ov2685_enum_frame_sizes,
+ .get_fmt = ov2685_get_fmt,
+ .set_fmt = ov2685_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov2685_subdev_ops = {
+ .core = &ov2685_core_ops,
+ .video = &ov2685_video_ops,
+ .pad = &ov2685_pad_ops,
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov2685_internal_ops = {
+ .open = ov2685_open,
+};
+#endif
+
+static const struct v4l2_ctrl_ops ov2685_ctrl_ops = {
+ .s_ctrl = ov2685_set_ctrl,
+};
+
+static int ov2685_initialize_controls(struct ov2685 *ov2685)
+{
+ const struct ov2685_mode *mode;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl *ctrl;
+ u64 exposure_max;
+ u32 pixel_rate, h_blank;
+ int ret;
+
+ handler = &ov2685->ctrl_handler;
+ mode = ov2685->cur_mode;
+ ret = v4l2_ctrl_handler_init(handler, 1);
+ if (ret)
+ return ret;
+ handler->lock = &ov2685->mutex;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ pixel_rate = (link_freq_menu_items[0] * 2 * OV2685_LANES) /
+ OV2685_BITS_PER_SAMPLE;
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, pixel_rate, 1, pixel_rate);
+
+ h_blank = mode->hts_def - mode->width;
+ ov2685->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov2685->hblank)
+ ov2685->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov2685->vblank = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_VBLANK, mode->vts_def - mode->height,
+ OV2685_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+
+ exposure_max = mode->vts_def - 4;
+ ov2685->exposure = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_EXPOSURE, OV2685_EXPOSURE_MIN,
+ exposure_max, OV2685_EXPOSURE_STEP,
+ mode->exp_def);
+
+ ov2685->anal_gain = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, OV2685_GAIN_MIN,
+ OV2685_GAIN_MAX, OV2685_GAIN_STEP,
+ OV2685_GAIN_DEFAULT);
+
+ ov2685->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+ &ov2685_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov2685_test_pattern_menu) - 1,
+ 0, 0, ov2685_test_pattern_menu);
+
+ if (handler->error) {
+ v4l2_ctrl_handler_free(handler);
+ return handler->error;
+ }
+
+ ov2685->subdev.ctrl_handler = handler;
+
+ return 0;
+}
+
+static int ov2685_check_sensor_id(struct ov2685 *ov2685,
+ struct i2c_client *client)
+{
+ struct device *dev = &ov2685->client->dev;
+ int id, ret;
+
+ ret = __ov2685_power_on(ov2685);
+ if (ret)
+ return ret;
+ ov2685_read_reg(client, OV2685_REG_CHIP_ID,
+ OV2685_REG_VALUE_16BIT, &id);
+ __ov2685_power_off(ov2685);
+
+ if (id != CHIP_ID) {
+ dev_err(dev, "Wrong camera sensor id(%04x)\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
+
+ return 0;
+}
+
+static int ov2685_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ov2685 *ov2685;
+ int ret;
+
+ ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
+ if (!ov2685)
+ return -ENOMEM;
+
+ ov2685->client = client;
+ ov2685->cur_mode = &supported_modes[0];
+
+ ov2685->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov2685->xvclk)) {
+ dev_err(dev, "Failed to get xvclk\n");
+ return -EINVAL;
+ }
+
+ ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov2685->reset_gpio)) {
+ dev_err(dev, "Failed to get reset-gpios\n");
+ return -EINVAL;
+ }
+
+ ov2685->avdd_regulator = devm_regulator_get(dev, "avdd");
+ if (IS_ERR(ov2685->avdd_regulator)) {
+ dev_err(dev, "Failed to get avdd-supply\n");
+ return -EINVAL;
+ }
+ ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd");
+ if (IS_ERR(ov2685->dovdd_regulator)) {
+ dev_err(dev, "Failed to get dovdd-supply\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&ov2685->mutex);
+ v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
+ ret = ov2685_initialize_controls(ov2685);
+ if (ret)
+ goto destroy_mutex;
+
+ ret = ov2685_check_sensor_id(ov2685, client);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ ov2685->subdev.internal_ops = &ov2685_internal_ops;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
+ if (ret < 0)
+ goto free_ctrl_handler;
+#endif
+
+ ret = v4l2_async_register_subdev(&ov2685->subdev);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto clean_entity;
+ }
+
+ pm_runtime_enable(dev);
+ return 0;
+
+clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&ov2685->subdev.entity);
+#endif
+free_ctrl_handler:
+ v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+destroy_mutex:
+ mutex_destroy(&ov2685->mutex);
+
+ return ret;
+}
+
+static int ov2685_remove(struct i2c_client *client)
+{
+ struct ov2685 *ov2685 = i2c_get_clientdata(client);
+
+ __ov2685_power_off(ov2685);
+ v4l2_async_unregister_subdev(&ov2685->subdev);
+ media_entity_cleanup(&ov2685->subdev.entity);
+ v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+ mutex_destroy(&ov2685->mutex);
+
+ return 0;
+}
+
+static const struct of_device_id ov2685_of_match[] = {
+ { .compatible = "ovti,ov2685" },
+ {},
+};
+
+static struct i2c_driver ov2685_i2c_driver = {
+ .driver = {
+ .name = "ov2685",
+ .owner = THIS_MODULE,
+ .pm = &ov2685_pm_ops,
+ .of_match_table = ov2685_of_match
+ },
+ .probe = &ov2685_probe,
+ .remove = &ov2685_remove,
+};
+
+module_i2c_driver(ov2685_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov2685 sensor driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 4/4] [media] dt/bindings: Add bindings for OV2685
From: Shunqian Zheng @ 2017-12-29 8:08 UTC (permalink / raw)
To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng
In-Reply-To: <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Add device tree binding documentation for the OV2685 sensor.
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
.../devicetree/bindings/media/i2c/ov2685.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ov2685.txt
diff --git a/Documentation/devicetree/bindings/media/i2c/ov2685.txt b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
new file mode 100644
index 0000000..85aec03
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
@@ -0,0 +1,35 @@
+* Omnivision OV2685 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: should be "ovti,ov2685"
+- clocks: reference to the 24M xvclk input clock.
+- clock-names: should be "xvclk".
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dvdd-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: Low active reset gpio
+
+The device node must contain one 'port' child node for its digital output
+video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+ ucam: camera-sensor@3c {
+ compatible = "ovti,ov2685";
+ reg = <0x3c>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&clk_24m_cam>;
+
+ clocks = <&cru SCLK_TESTCLKOUT1>;
+ clock-names = "xvclk";
+
+ avdd-supply = <&pp2800_cam>;
+ dovdd-supply = <&pp1800>;
+ reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
+
+ port {
+ ucam_out: endpoint {
+ remote-endpoint = <&mipi_in_ucam>;
+ data-lanes = <1>;
+ };
+ };
+ };
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v3 09/11] clk: sunxi-ng: add support for Allwinner A64 DE2 CCU
From: Chen-Yu Tsai @ 2017-12-29 8:35 UTC (permalink / raw)
To: Icenowy Zheng
Cc: devicetree, linux-kernel, dri-devel, Chen-Yu Tsai, Rob Herring,
Maxime Ripard, linux-clk, linux-arm-kernel
In-Reply-To: <20171222122243.25735-10-icenowy@aosc.io>
On Fri, Dec 22, 2017 at 8:22 PM, Icenowy Zheng <icenowy@aosc.io> wrote:
> Allwinner A64's DE2 needs to claim a section of SRAM (SRAM C) to work.
>
> Add support for it.
>
> Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
> ---
> drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 32 ++++++++++++++++++++++++--------
> 1 file changed, 24 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
> index 468d1abaf0ee..38b029b7bb5a 100644
> --- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
> +++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
> @@ -17,6 +17,7 @@
> #include <linux/of_platform.h>
> #include <linux/platform_device.h>
> #include <linux/reset.h>
> +#include <linux/soc/sunxi/sunxi_sram.h>
>
> #include "ccu_common.h"
> #include "ccu_div.h"
> @@ -196,6 +197,11 @@ static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = {
> .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets),
> };
>
> +static bool sunxi_de2_clk_has_sram(const struct device_node *node)
> +{
> + return of_device_is_compatible(node, "allwinner,sun50i-a64-de2-clk");
Having to maintain a separate list is not a great idea. Please consider
adding a de2 ccu specific structure that embeds a struct sunxi_ccu_desc,
and also includes whatever special quirks flags this needs.
ChenYu
> +}
> +
> static int sunxi_de2_clk_probe(struct platform_device *pdev)
> {
> struct resource *res;
> @@ -239,11 +245,20 @@ static int sunxi_de2_clk_probe(struct platform_device *pdev)
> return ret;
> }
>
> + if (sunxi_de2_clk_has_sram(pdev->dev.of_node)) {
> + ret = sunxi_sram_claim(&pdev->dev);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "Error couldn't map SRAM to device\n");
> + return ret;
> + }
> + }
> +
> /* The clocks need to be enabled for us to access the registers */
> ret = clk_prepare_enable(bus_clk);
> if (ret) {
> dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
> - return ret;
> + goto err_release_sram;
> }
>
> ret = clk_prepare_enable(mod_clk);
> @@ -272,6 +287,10 @@ static int sunxi_de2_clk_probe(struct platform_device *pdev)
> clk_disable_unprepare(mod_clk);
> err_disable_bus_clk:
> clk_disable_unprepare(bus_clk);
> +err_release_sram:
> + if (sunxi_de2_clk_has_sram(pdev->dev.of_node))
> + sunxi_sram_release(&pdev->dev);
> +
> return ret;
> }
>
> @@ -288,17 +307,14 @@ static const struct of_device_id sunxi_de2_clk_ids[] = {
> .compatible = "allwinner,sun8i-v3s-de2-clk",
> .data = &sun8i_v3s_de2_clk_desc,
> },
> + {
> + .compatible = "allwinner,sun50i-a64-de2-clk",
> + .data = &sun50i_a64_de2_clk_desc,
> + },
> {
> .compatible = "allwinner,sun50i-h5-de2-clk",
> .data = &sun50i_a64_de2_clk_desc,
> },
> - /*
> - * The Allwinner A64 SoC needs some bit to be poke in syscon to make
> - * DE2 really working.
> - * So there's currently no A64 compatible here.
> - * H5 shares the same reset line with A64, so here H5 is using the
> - * clock description of A64.
> - */
> { }
> };
>
> --
> 2.14.2
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH v3 07/11] ARM: sunxi: h3/h5: add simplefb nodes
From: Chen-Yu Tsai @ 2017-12-29 8:45 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Rob Herring, Maxime Ripard, Chen-Yu Tsai, linux-clk, devicetree,
linux-arm-kernel, linux-kernel, dri-devel
In-Reply-To: <20171222122243.25735-8-icenowy-h8G6r0blFSE@public.gmane.org>
On Fri, Dec 22, 2017 at 8:22 PM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> The H3/H5 SoCs have a HDMI output and a TV Composite output.
>
> Add simplefb nodes for these outputs.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
> ---
> arch/arm/boot/dts/sunxi-h3-h5.dtsi | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sunxi-h3-h5.dtsi b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> index fcb909658cf0..31478c03790d 100644
> --- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> +++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
> @@ -53,6 +53,35 @@
> #address-cells = <1>;
> #size-cells = <1>;
>
> + chosen {
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> +
> + framebuffer-hdmi {
> + compatible = "allwinner,simple-framebuffer",
> + "simple-framebuffer";
> + allwinner,pipeline = "mixer0-lcd0-hdmi";
> + clocks = <&display_clocks CLK_BUS_MIXER0>,
> + <&ccu CLK_BUS_TCON0>, <&ccu CLK_BUS_HDMI>,
Are the bus clocks necessary? It's not like simplefb is going to access
any of the control registers.
> + <&display_clocks CLK_MIXER0>,
> + <&ccu CLK_TCON0>, <&ccu CLK_HDMI>,
> + <&ccu CLK_HDMI_DDC>;
Is the DDC clock necessary? There's no usage of DDC with simplefb.
ChenYu
> + status = "disabled";
> + };
> +
> + framebuffer-tve {
> + compatible = "allwinner,simple-framebuffer",
> + "simple-framebuffer";
> + allwinner,pipeline = "mixer1-lcd1-tve";
> + clocks = <&display_clocks CLK_BUS_MIXER1>,
> + <&ccu CLK_BUS_TCON1>, <&ccu CLK_BUS_TVE>,
> + <&display_clocks CLK_MIXER1>,
> + <&ccu CLK_TVE>;
> + status = "disabled";
> + };
> + };
> +
> clocks {
> #address-cells = <1>;
> #size-cells = <1>;
> --
> 2.14.2
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 00/11] Allwinner H3/H5/A64(DE2) SimpleFB support
From: Chen-Yu Tsai @ 2017-12-29 8:52 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Rob Herring, Maxime Ripard, Chen-Yu Tsai, linux-clk, devicetree,
linux-arm-kernel, linux-kernel, dri-devel
In-Reply-To: <20171222122243.25735-1-icenowy-h8G6r0blFSE@public.gmane.org>
On Fri, Dec 22, 2017 at 8:22 PM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> This patchset adds support for the SimpleFB on Allwinner SoCs with
> "Display Engine 2.0".
>
> PATCH 1 to PATCH 3 are DE2 CCU fixes for H3/H5 SoCs.
>
> PATCH 4 adds the pipeline strings for DE2 SimpleFB.
>
> PATCH 5 to 7 adds necessary device tree nodes (DE2 CCU and SimpleFB)
> for H3/H5 SoCs.
>
> PATCH 8 to 11 are for Allwinner A64 SoC to enable SimpleFB.
>
> Icenowy Zheng (11):
> dt-bindings: fix the binding of Allwinner DE2 CCU of A83T and H3
> clk: sunxi-ng: add support for Allwinner H3 DE2 CCU
> clk: sunxi-ng: fix the A64/H5 clock description of DE2 CCU
> dt-bindings: simplefb-sunxi: add pipelines for DE2
> ARM: sun8i: h3/h5: add DE2 CCU device node for H3
> arm64: allwinner: h5: add compatible string for DE2 CCU
Applied the first six patches. There is no guarantee they will make
it into 4.16.
ChenYu
> ARM: sunxi: h3/h5: add simplefb nodes
> dt-bindings: add binding for A64 DE2 CCU SRAM
> clk: sunxi-ng: add support for Allwinner A64 DE2 CCU
> arm64: allwinner: a64: add DE2 CCU for A64 SoC
> arm64: allwinner: a64: add simplefb for A64 SoC
>
> .../devicetree/bindings/clock/sun8i-de2.txt | 10 ++-
> .../bindings/display/simple-framebuffer-sunxi.txt | 4 +
> arch/arm/boot/dts/sun8i-h3.dtsi | 4 +
> arch/arm/boot/dts/sunxi-h3-h5.dtsi | 43 +++++++++++
> arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 65 +++++++++++++++++
> arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 4 +
> drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 85 +++++++++++++++++++---
> 7 files changed, 202 insertions(+), 13 deletions(-)
>
> --
> 2.14.2
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [linux-sunxi] [PATCH] ARM: dts: sun8i: fix USB Ethernet of Orange Pi R1
From: Chen-Yu Tsai @ 2017-12-29 8:55 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Maxime Ripard, Chen-Yu Tsai, devicetree, linux-arm-kernel,
linux-kernel, linux-sunxi
In-Reply-To: <20171228140538.14388-1-icenowy-h8G6r0blFSE@public.gmane.org>
On Thu, Dec 28, 2017 at 10:05 PM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> Orange Pi R1 uses a Realtek RTL8152B USB Ethernet chip, which is easily
> seen on the board but not show in the schematics. A regulator for the
> power of the RTL8152B chip is hidden, which uses the same pin with the
> Wi-Fi regulator on the original Orange Pi Zero.
>
> Add this regulator back to the device tree, and bind it to USB1.
>
> Tested-by: Hauke Mehrtens <hauke-5/S+JYg5SzeELgA04lAiVw@public.gmane.org>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
Applied. There is no guarantee this will make it into 4.16-rc1. If not,
this will be sent later on as a fix for 4.16.
ChenYu
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [linux-sunxi] [PATCH] ARM: dts: sun8i: fix USB Ethernet of Orange Pi R1
From: Icenowy Zheng @ 2017-12-29 8:56 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Maxime Ripard, devicetree, linux-arm-kernel, linux-kernel,
linux-sunxi
In-Reply-To: <CAGb2v679BdkA=4hzsTiHXwqdSop+dFDTePqmd_eNSMQR+gq9fg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
于 2017年12月29日 GMT+08:00 下午4:55:34, Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org> 写到:
>On Thu, Dec 28, 2017 at 10:05 PM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
>wrote:
>> Orange Pi R1 uses a Realtek RTL8152B USB Ethernet chip, which is
>easily
>> seen on the board but not show in the schematics. A regulator for the
>> power of the RTL8152B chip is hidden, which uses the same pin with
>the
>> Wi-Fi regulator on the original Orange Pi Zero.
>>
>> Add this regulator back to the device tree, and bind it to USB1.
>>
>> Tested-by: Hauke Mehrtens <hauke-5/S+JYg5SzeELgA04lAiVw@public.gmane.org>
>> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
>
>Applied. There is no guarantee this will make it into 4.16-rc1. If not,
>this will be sent later on as a fix for 4.16.
Because of New Year?
>
>ChenYu
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] ARM: dts: sun8i: fix USB Ethernet of Orange Pi R1
From: Chen-Yu Tsai @ 2017-12-29 8:59 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Chen-Yu Tsai, Maxime Ripard, devicetree, linux-arm-kernel,
linux-kernel, linux-sunxi
In-Reply-To: <511FDCF7-4B8D-4270-8E51-FF723AF07082-h8G6r0blFSE@public.gmane.org>
On Fri, Dec 29, 2017 at 4:56 PM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
>
>
> 于 2017年12月29日 GMT+08:00 下午4:55:34, Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org> 写到:
>>On Thu, Dec 28, 2017 at 10:05 PM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
>>wrote:
>>> Orange Pi R1 uses a Realtek RTL8152B USB Ethernet chip, which is
>>easily
>>> seen on the board but not show in the schematics. A regulator for the
>>> power of the RTL8152B chip is hidden, which uses the same pin with
>>the
>>> Wi-Fi regulator on the original Orange Pi Zero.
>>>
>>> Add this regulator back to the device tree, and bind it to USB1.
>>>
>>> Tested-by: Hauke Mehrtens <hauke-5/S+JYg5SzeELgA04lAiVw@public.gmane.org>
>>> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
>>
>>Applied. There is no guarantee this will make it into 4.16-rc1. If not,
>>this will be sent later on as a fix for 4.16.
>
> Because of New Year?
Yeah. I've already sent the pull requests for 4.16. Anything applied
after that I want tested in -next for a few days. By then we'd be
past -rc6, and anything sent after that as late pull requests has a
chance of not being merged.
Plus the arm-soc maintainers are still on holiday.
ChenYu
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH 0/3] mtd: spi-nor: fix DMA-unsafe buffer issue between MTD and SPI
From: Vignesh R @ 2017-12-29 9:16 UTC (permalink / raw)
To: Trent Piepho, linux-mtd@lists.infradead.org,
linux@armlinux.org.uk, broonie@kernel.org,
cyrille.pitchen@wedev4u.fr, dwmw2@infradead.org,
computersforpeace@gmail.com, boris.brezillon@free-electrons.com,
richard@nod.at, marek.vasut@gmail.com
Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
nicolas.ferre@microchip.com, robh@kernel.org,
radu.pirea@microchip.com, devicetree@vger.kernel.org
In-Reply-To: <1514313927.26695.19.camel@impinj.com>
On Wednesday 27 December 2017 12:15 AM, Trent Piepho wrote:
> On Sun, 2017-12-24 at 05:36 +0100, Cyrille Pitchen wrote:
>> this series tries to solve a long time issue of compatibility between the
>> MTD and SPI sub-systems about whether we should use DMA-safe memory.
>
> Can this should replace SoC specific fixes like:
>
> c687c46e9e45
> spi: spi-ti-qspi: Use bounce buffer if read buffer is not DMA'ble
>
Yes, I plan to revert this patch once m25p80 starts using bounce buffers.
I am interested in knowing which other SPI clients end up passing non
DMA'able buffers. AFAIK, its UBIFS and JFFS2. Most other SPI devices on
TI boards that I have dealt with like Touch, Sensors (IIO), GPIO
expanders etc all provide DMA'able buffers.
--
Regards
Vignesh
^ permalink raw reply
* Re: [PATCH v2 7/9] PCI: endpoint: Add the function number as argument to EPC ops
From: Kishon Vijay Abraham I @ 2017-12-29 9:23 UTC (permalink / raw)
To: Cyrille Pitchen, bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
lorenzo.pieralisi-5wv7dgnIgG8, linux-pci-u79uwXL29TY76Z2rM5mHXA
Cc: adouglas-vna1KIf7WgpBDgjK7y7TUQ, stelford-vna1KIf7WgpBDgjK7y7TUQ,
dgary-vna1KIf7WgpBDgjK7y7TUQ, kgopi-vna1KIf7WgpBDgjK7y7TUQ,
eandrews-vna1KIf7WgpBDgjK7y7TUQ,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
sureshp-vna1KIf7WgpBDgjK7y7TUQ, nsekhar-l0cyMroinI0,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <425171aaba3a9e8ea68b9e94f37b1c97e8cf9861.1513620412.git.cyrille.pitchen-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Hi,
On Monday 18 December 2017 11:46 PM, Cyrille Pitchen wrote:
> This patch updates the prototype of most handlers from 'struct
> pci_epc_ops' so the EPC library can now support multi-function devices.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
> drivers/pci/dwc/pcie-designware-ep.c | 20 +++++----
> drivers/pci/endpoint/functions/pci-epf-test.c | 41 ++++++++++--------
> drivers/pci/endpoint/pci-epc-core.c | 62 ++++++++++++++++-----------
> include/linux/pci-epc.h | 43 +++++++++++--------
> 4 files changed, 96 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c
> index d53d5f168363..7a573d8bb62d 100644
> --- a/drivers/pci/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/dwc/pcie-designware-ep.c
> @@ -39,7 +39,7 @@ static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
> dw_pcie_writel_dbi(pci, reg, 0x0);
> }
>
> -static int dw_pcie_ep_write_header(struct pci_epc *epc,
> +static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
> struct pci_epf_header *hdr)
> {
> struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> @@ -112,7 +112,8 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr,
> return 0;
> }
>
> -static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
> +static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
> + enum pci_barno bar)
> {
> struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> @@ -124,7 +125,8 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
> clear_bit(atu_index, &ep->ib_window_map);
> }
>
> -static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
> +static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
> + enum pci_barno bar,
> dma_addr_t bar_phys, size_t size, int flags)
> {
> int ret;
> @@ -163,7 +165,8 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
> return -EINVAL;
> }
>
> -static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
> +static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr)
> {
> int ret;
> u32 atu_index;
> @@ -178,7 +181,8 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
> clear_bit(atu_index, &ep->ob_window_map);
> }
>
> -static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
> +static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr,
> u64 pci_addr, size_t size)
> {
> int ret;
> @@ -194,7 +198,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
> return 0;
> }
>
> -static int dw_pcie_ep_get_msi(struct pci_epc *epc)
> +static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
> {
> int val;
> u32 lower_addr;
> @@ -214,7 +218,7 @@ static int dw_pcie_ep_get_msi(struct pci_epc *epc)
> return val;
> }
>
> -static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int)
> +static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int)
> {
> int val;
> struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> @@ -226,7 +230,7 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int)
> return 0;
> }
>
> -static int dw_pcie_ep_raise_irq(struct pci_epc *epc,
> +static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
> enum pci_epc_irq_type type, u8 interrupt_num)
> {
> struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
> index f9308c2f22e6..7bacca8daec6 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-test.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-test.c
> @@ -104,7 +104,8 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test)
> goto err;
> }
>
> - ret = pci_epc_map_addr(epc, src_phys_addr, reg->src_addr, reg->size);
> + ret = pci_epc_map_addr(epc, epf->func_no, src_phys_addr, reg->src_addr,
> + reg->size);
> if (ret) {
> dev_err(dev, "failed to map source address\n");
> reg->status = STATUS_SRC_ADDR_INVALID;
> @@ -119,7 +120,8 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test)
> goto err_src_map_addr;
> }
>
> - ret = pci_epc_map_addr(epc, dst_phys_addr, reg->dst_addr, reg->size);
> + ret = pci_epc_map_addr(epc, epf->func_no, dst_phys_addr, reg->dst_addr,
> + reg->size);
> if (ret) {
> dev_err(dev, "failed to map destination address\n");
> reg->status = STATUS_DST_ADDR_INVALID;
> @@ -128,13 +130,13 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test)
>
> memcpy(dst_addr, src_addr, reg->size);
>
> - pci_epc_unmap_addr(epc, dst_phys_addr);
> + pci_epc_unmap_addr(epc, epf->func_no, dst_phys_addr);
>
> err_dst_addr:
> pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size);
>
> err_src_map_addr:
> - pci_epc_unmap_addr(epc, src_phys_addr);
> + pci_epc_unmap_addr(epc, epf->func_no, src_phys_addr);
>
> err_src_addr:
> pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size);
> @@ -164,7 +166,8 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test)
> goto err;
> }
>
> - ret = pci_epc_map_addr(epc, phys_addr, reg->src_addr, reg->size);
> + ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->src_addr,
> + reg->size);
> if (ret) {
> dev_err(dev, "failed to map address\n");
> reg->status = STATUS_SRC_ADDR_INVALID;
> @@ -186,7 +189,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test)
> kfree(buf);
>
> err_map_addr:
> - pci_epc_unmap_addr(epc, phys_addr);
> + pci_epc_unmap_addr(epc, epf->func_no, phys_addr);
>
> err_addr:
> pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size);
> @@ -215,7 +218,8 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)
> goto err;
> }
>
> - ret = pci_epc_map_addr(epc, phys_addr, reg->dst_addr, reg->size);
> + ret = pci_epc_map_addr(epc, epf->func_no, phys_addr, reg->dst_addr,
> + reg->size);
> if (ret) {
> dev_err(dev, "failed to map address\n");
> reg->status = STATUS_DST_ADDR_INVALID;
> @@ -242,7 +246,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)
> kfree(buf);
>
> err_map_addr:
> - pci_epc_unmap_addr(epc, phys_addr);
> + pci_epc_unmap_addr(epc, epf->func_no, phys_addr);
>
> err_addr:
> pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size);
> @@ -260,11 +264,11 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq)
> struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
>
> reg->status |= STATUS_IRQ_RAISED;
> - msi_count = pci_epc_get_msi(epc);
> + msi_count = pci_epc_get_msi(epc, epf->func_no);
> if (irq > msi_count || msi_count <= 0)
> - pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0);
> + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0);
> else
> - pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq);
> + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq);
> }
>
> static void pci_epf_test_cmd_handler(struct work_struct *work)
> @@ -291,7 +295,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
>
> if (command & COMMAND_RAISE_LEGACY_IRQ) {
> reg->status = STATUS_IRQ_RAISED;
> - pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0);
> + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_LEGACY, 0);
> goto reset_handler;
> }
>
> @@ -326,11 +330,11 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
> }
>
> if (command & COMMAND_RAISE_MSI_IRQ) {
> - msi_count = pci_epc_get_msi(epc);
> + msi_count = pci_epc_get_msi(epc, epf->func_no);
> if (irq > msi_count || msi_count <= 0)
> goto reset_handler;
> reg->status = STATUS_IRQ_RAISED;
> - pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq);
> + pci_epc_raise_irq(epc, epf->func_no, PCI_EPC_IRQ_MSI, irq);
> goto reset_handler;
> }
>
> @@ -358,7 +362,7 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
> for (bar = BAR_0; bar <= BAR_5; bar++) {
> if (epf_test->reg[bar]) {
> pci_epf_free_space(epf, epf_test->reg[bar], bar);
> - pci_epc_clear_bar(epc, bar);
> + pci_epc_clear_bar(epc, epf->func_no, bar);
> }
> }
> }
> @@ -380,7 +384,8 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
>
> for (bar = BAR_0; bar <= BAR_5; bar++) {
> epf_bar = &epf->bar[bar];
> - ret = pci_epc_set_bar(epc, bar, epf_bar->phys_addr,
> + ret = pci_epc_set_bar(epc, epf->func_no, bar,
> + epf_bar->phys_addr,
> epf_bar->size, flags);
> if (ret) {
> pci_epf_free_space(epf, epf_test->reg[bar], bar);
> @@ -433,7 +438,7 @@ static int pci_epf_test_bind(struct pci_epf *epf)
> if (WARN_ON_ONCE(!epc))
> return -EINVAL;
>
> - ret = pci_epc_write_header(epc, header);
> + ret = pci_epc_write_header(epc, epf->func_no, header);
> if (ret) {
> dev_err(dev, "configuration header write failed\n");
> return ret;
> @@ -447,7 +452,7 @@ static int pci_epf_test_bind(struct pci_epf *epf)
> if (ret)
> return ret;
>
> - ret = pci_epc_set_msi(epc, epf->msi_interrupts);
> + ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
> if (ret)
> return ret;
>
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> index cd7d4788b94d..77420364a728 100644
> --- a/drivers/pci/endpoint/pci-epc-core.c
> +++ b/drivers/pci/endpoint/pci-epc-core.c
> @@ -141,25 +141,26 @@ EXPORT_SYMBOL_GPL(pci_epc_start);
> /**
> * pci_epc_raise_irq() - interrupt the host system
> * @epc: the EPC device which has to interrupt the host
> + * @func_no: the endpoint function number in the EPC device
> * @type: specify the type of interrupt; legacy or MSI
> * @interrupt_num: the MSI interrupt number
> *
> * Invoke to raise an MSI or legacy interrupt
> */
> -int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type,
> - u8 interrupt_num)
> +int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
> + enum pci_epc_irq_type type, u8 interrupt_num)
> {
> int ret;
> unsigned long flags;
>
> - if (IS_ERR(epc))
> + if (IS_ERR(epc) || func_no > BAR_5)
why is function number compared with BAR? here and everywhere below..
Thanks
Kishon
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/3] mtd: spi-nor: add optional DMA-safe bounce buffer for data transfer
From: Vignesh R @ 2017-12-29 10:16 UTC (permalink / raw)
To: Trent Piepho,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org,
broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
cyrille.pitchen-yU5RGvR974pGWvitb5QawA@public.gmane.org,
dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org,
computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org,
richard-/L3Ra7n9ekc@public.gmane.org,
marek.vasut-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org,
radu.pirea-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org,
robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1514487276.26695.94.camel-cgc2CodaaHDQT0dZR+AlfA@public.gmane.org>
On Friday 29 December 2017 12:24 AM, Trent Piepho wrote:
> On Thu, 2017-12-28 at 11:39 +0100, Cyrille Pitchen wrote:
>> Le 26/12/2017 à 20:43, Trent Piepho a écrit :
>> > On Sun, 2017-12-24 at 05:36 +0100, Cyrille Pitchen wrote:
>> > >
>> > > Then the patch adds two hardware capabilities for SPI flash controllers,
>> > > SNOR_HWCAPS_WR_BOUNCE and SNOR_HWCAPS_RD_BOUNCE.
>> >
>> > Are there any drivers for which a bounce buffer is NOT needed when the
>> > tx/rx buffer is not in DMA safe memory? Maybe it would make more sense
>> > to invert the sense of these flags, so that they indicate the driver
>> > does not need DMA safe buffers, if that is the uncommon/non-existent
>> > case, so that fewer drivers need to be modified to to be fixed?
>> >
>>
>> It doesn't sound safe for a first step. I don't know if some of the
>> spi-flash controllers are embedded inside systems with small memory and
>> don't care about DMA transfers. Maybe they don't plan to use anything else
>> but PIO transfers. Then why taking the risk to exhaust the memory on systems
>> that would not use the bounce buffer anyway?
>
> This would certainly be the case when the driver does not even support
> DMA in the first place.
>
> This also makes me wonder, how inefficient does this become when it
> uses a bounce buffer for small transfer that would not use DMA anyway?
> In the spi_flash_read() interface for spi masters, there is a master
> method spi_flash_can_dma() callback used by the spi core to tell if
> each transfer can be DMAed.
>
> Should something like that be used here, to ask the master if it would
> use dma on the buffer?
>
> This might also prevent allocation of the bounce buffer if the only DMA
> unsafe transfers are tiny control ops with stack variables that
> wouldn't use DMA, e.g. the stuff spi_nor_read_sfdp_dma_unsafe() does.
>
>
>> About the memory loss when forcing the SNOR_HWCAPS_*_BOUNCE in m25p80.c, I
>> justify it because the m25p80 has to be compliant with the SPI sub-system
>> requirements but currently is not. However I've taken care not to allocate
>> the bounce buffer as long as we use only DMA-safe buffers.
>
> Another possibility to reduce memory usage: make the buffer smaller
> when first allocated by being just enough for the needed space. Grow
> it (by powers of two?) until it reaches the max allowed size. No
> reason to use a 256 kB buffer if DMA unsafe operations never get that
> big.
>
>
>> Here at the MTD side, the main (only ?) source of DMA-unsafe buffers is
>> the UBIFS (JFFS2 too ?) layer. Then I've assumed that systems using such a
>> file-system should already have enough system memory.
>
> I saw a note in one of the existing DMA fixes that JFFS2 was the source
> of the unsafe buffers, so probably there too.
>
>
>>
>> Vignesh has suggested to call virt_addr_valid() instead.
>> I think Boris has also told me about this function.
>> So it might be the right solution. What do you think about their proposal?
>
> Not sure what exactly the differences are between these methods. The
> fact that each of the many existing DMA fixes uses slightly different
> code to detect what is unsafe speaks to the difficulty of this problem!
My understanding based on Documentation/DMA-API-HOWTO.txt and
Documentation/arm/memory.txt is that
virt_addr_valid() will guarantee that address is in range of
PAGE_OFFSET to high_memory-1 (Kernel direct-mapped RAM region) which is
address range of buffers that are DMA'able.
> virt_addr_valid() is already used by spi-ti-qspi. spi core uses for
> the buffer map helper, but that code path is for buffers which are NOT
> vmalloc or highmem, but are still not virt_addr_valid() for some other
> reason.
>
if (vmalloced_buf || kmap_buf) {
/* Handle vmalloc'd or kmap'd buffers */
...
} else if (virt_addr_valid(buf)) {
/* Handle kmalloc'd and such buffers */
...
} else {
/* Error if none of the above */
return -EINVAL;
}
--
Regards
Vignesh
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2 0/5] Sunxi: Add SMP support on A83T
From: Mylène Josserand @ 2017-12-29 10:55 UTC (permalink / raw)
To: maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, wens-jdAy2FN1RRM,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
Hello everyone,
This is a V2 of my series that adds SMP support for Allwinner sun8i-a83t
with MCPM (Multi-Cluster Power Management).
Based on last linux-next (next-20171222).
Changes since v1:
- Add Chen Yu's patch in my series (see path 01)
- Add new compatibles for prcm and cpucfg registers for sun8i-a83t.
Create two functions to separate the DT parsing of sun9i-a80 and
sun8i-a83t.
- Thanks to Maxime's review: order device tree's nodes according
to physical addresses, remove unused label and fix registers' sizes.
Update the commit log and commit title of my last patch (see
patch 05).
Patch 01: Patch from Chen Yu Tsai that adds SMP support on A80
with MCPM
Patch 02: Convert the MCPM driver to use it for A83T. This SoC has a
bit flip that needs to be handled.
Patch 03: Add registers nodes (prcm, cpucfg and r_cpucfg) needed
for MCPM.
Patch 04: Add CCI-400 node for a83t.
Patch 05: Fix the use of virtual timers that hangs the kernel in
case of SMP support.
If you have any remarks/questions, let me know.
Thank you in advance,
Mylène
Chen-Yu Tsai (1):
ARM: sun9i: Support SMP on A80 with Multi-Cluster Power Management
(MCPM)
Mylène Josserand (4):
ARM: sunxi: mcpm: Add support for A83T
arm: dts: sun8i: a83t: Add registers needed for MCPM
arm: dts: sun8i: a83t: Add CCI-400 node
arm: dts: sun8i: a83t: Fix undefined offset with virtual timer
arch/arm/boot/dts/sun8i-a83t.dtsi | 57 +++++
arch/arm/mach-sunxi/Kconfig | 11 +
arch/arm/mach-sunxi/Makefile | 1 +
arch/arm/mach-sunxi/mcpm.c | 513 ++++++++++++++++++++++++++++++++++++++
4 files changed, 582 insertions(+)
create mode 100644 arch/arm/mach-sunxi/mcpm.c
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2 1/5] ARM: sun9i: Support SMP on A80 with Multi-Cluster Power Management (MCPM)
From: Mylène Josserand @ 2017-12-29 10:55 UTC (permalink / raw)
To: maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, wens-jdAy2FN1RRM,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
In-Reply-To: <20171229105506.24851-1-mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
From: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
The A80 is a big.LITTLE SoC with 1 cluster of 4 Cortex-A7s and
1 cluster of 4 Cortex-A15s.
This patch adds support to bring up the second cluster and thus all
cores using the common MCPM code. Core/cluster power down has not
been implemented, thus CPU hotplugging and big.LITTLE switcher is
not supported.
Signed-off-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
Signed-off-by: Mylène Josserand <mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm/mach-sunxi/Kconfig | 10 ++
arch/arm/mach-sunxi/Makefile | 1 +
arch/arm/mach-sunxi/mcpm.c | 391 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 402 insertions(+)
create mode 100644 arch/arm/mach-sunxi/mcpm.c
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 58153cdf025b..177380548d99 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -47,5 +47,15 @@ config MACH_SUN9I
bool "Allwinner (sun9i) SoCs support"
default ARCH_SUNXI
select ARM_GIC
+ imply MCPM
+
+config SUN9I_A80_MCPM
+ bool "Allwinner A80 Multi-Cluster PM support"
+ depends on MCPM && MACH_SUN9I
+ default MACH_SUN9I
+ select ARM_CCI400_PORT_CTRL
+ help
+ This is needed to provide CPU and cluster power management
+ on Allwinner A80 implementing big.LITTLE.
endif
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 27b168f121a1..e8558912c714 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
obj-$(CONFIG_SMP) += platsmp.o
+obj-$(CONFIG_SUN9I_A80_MCPM) += mcpm.o
diff --git a/arch/arm/mach-sunxi/mcpm.c b/arch/arm/mach-sunxi/mcpm.c
new file mode 100644
index 000000000000..4b6e1d6ae379
--- /dev/null
+++ b/arch/arm/mach-sunxi/mcpm.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2015 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
+ *
+ * arch/arm/mach-sunxi/mcpm.c
+ *
+ * Based on arch/arm/mach-exynos/mcpm-exynos.c and Allwinner code
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/arm-cci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+
+#include <asm/cputype.h>
+#include <asm/cp15.h>
+#include <asm/mcpm.h>
+
+#define SUNXI_CPUS_PER_CLUSTER 4
+#define SUNXI_NR_CLUSTERS 2
+
+#define SUN9I_A80_A15_CLUSTER 1
+
+#define CPUCFG_CX_CTRL_REG0(c) (0x10 * (c))
+#define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE(n) BIT(n)
+#define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE_ALL 0xf
+#define CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A7 BIT(4)
+#define CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A15 BIT(0)
+#define CPUCFG_CX_CTRL_REG1(c) (0x10 * (c) + 0x4)
+#define CPUCFG_CX_CTRL_REG1_ACINACTM BIT(0)
+#define CPUCFG_CX_RST_CTRL(c) (0x80 + 0x4 * (c))
+#define CPUCFG_CX_RST_CTRL_DBG_SOC_RST BIT(24)
+#define CPUCFG_CX_RST_CTRL_ETM_RST(n) BIT(20 + (n))
+#define CPUCFG_CX_RST_CTRL_ETM_RST_ALL (0xf << 20)
+#define CPUCFG_CX_RST_CTRL_DBG_RST(n) BIT(16 + (n))
+#define CPUCFG_CX_RST_CTRL_DBG_RST_ALL (0xf << 16)
+#define CPUCFG_CX_RST_CTRL_H_RST BIT(12)
+#define CPUCFG_CX_RST_CTRL_L2_RST BIT(8)
+#define CPUCFG_CX_RST_CTRL_CX_RST(n) BIT(4 + (n))
+#define CPUCFG_CX_RST_CTRL_CORE_RST(n) BIT(n)
+
+#define PRCM_CPU_PO_RST_CTRL(c) (0x4 + 0x4 * (c))
+#define PRCM_CPU_PO_RST_CTRL_CORE(n) BIT(n)
+#define PRCM_CPU_PO_RST_CTRL_CORE_ALL 0xf
+#define PRCM_PWROFF_GATING_REG(c) (0x100 + 0x4 * (c))
+#define PRCM_PWROFF_GATING_REG_CLUSTER BIT(4)
+#define PRCM_PWROFF_GATING_REG_CORE(n) BIT(n)
+#define PRCM_PWR_SWITCH_REG(c, cpu) (0x140 + 0x10 * (c) + 0x4 * (cpu))
+#define PRCM_CPU_SOFT_ENTRY_REG 0x164
+
+static void __iomem *cpucfg_base;
+static void __iomem *prcm_base;
+
+static int sunxi_cpu_power_switch_set(unsigned int cpu, unsigned int cluster,
+ bool enable)
+{
+ u32 reg;
+
+ /* control sequence from Allwinner A80 user manual v1.2 PRCM section */
+ reg = readl(prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
+ if (enable) {
+ if (reg == 0x00) {
+ pr_debug("power clamp for cluster %u cpu %u already open\n",
+ cluster, cpu);
+ return 0;
+ }
+
+ writel(0xff, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
+ udelay(10);
+ writel(0xfe, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
+ udelay(10);
+ writel(0xf8, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
+ udelay(10);
+ writel(0xf0, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
+ udelay(10);
+ writel(0x00, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
+ udelay(10);
+ } else {
+ writel(0xff, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
+ udelay(10);
+ }
+
+ return 0;
+}
+
+static int sunxi_cpu_powerup(unsigned int cpu, unsigned int cluster)
+{
+ u32 reg;
+
+ pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+ if (cpu >= SUNXI_CPUS_PER_CLUSTER || cluster >= SUNXI_NR_CLUSTERS)
+ return -EINVAL;
+
+ /* assert processor power-on reset */
+ reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+ reg &= ~PRCM_CPU_PO_RST_CTRL_CORE(cpu);
+ writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+
+ /* Cortex-A7: hold L1 reset disable signal low */
+ if (!(of_machine_is_compatible("allwinner,sun9i-a80") &&
+ cluster == SUN9I_A80_A15_CLUSTER)) {
+ reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));
+ reg &= ~CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE(cpu);
+ writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));
+ }
+
+ /* assert processor related resets */
+ reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+ reg &= ~CPUCFG_CX_RST_CTRL_DBG_RST(cpu);
+
+ /*
+ * Allwinner code also asserts resets for NEON on A15. According
+ * to ARM manuals, asserting power-on reset is sufficient.
+ */
+ if (!(of_machine_is_compatible("allwinner,sun9i-a80") &&
+ cluster == SUN9I_A80_A15_CLUSTER)) {
+ reg &= ~CPUCFG_CX_RST_CTRL_ETM_RST(cpu);
+ }
+ writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+
+ /* open power switch */
+ sunxi_cpu_power_switch_set(cpu, cluster, true);
+
+ /* clear processor power gate */
+ reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
+ reg &= ~PRCM_PWROFF_GATING_REG_CORE(cpu);
+ writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
+ udelay(20);
+
+ /* de-assert processor power-on reset */
+ reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+ reg |= PRCM_CPU_PO_RST_CTRL_CORE(cpu);
+ writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+
+ /* de-assert all processor resets */
+ reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+ reg |= CPUCFG_CX_RST_CTRL_DBG_RST(cpu);
+ reg |= CPUCFG_CX_RST_CTRL_CORE_RST(cpu);
+ if (!(of_machine_is_compatible("allwinner,sun9i-a80") &&
+ cluster == SUN9I_A80_A15_CLUSTER)) {
+ reg |= CPUCFG_CX_RST_CTRL_ETM_RST(cpu);
+ } else {
+ reg |= CPUCFG_CX_RST_CTRL_CX_RST(cpu); /* NEON */
+ }
+ writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+
+ return 0;
+}
+
+static int sunxi_cluster_powerup(unsigned int cluster)
+{
+ u32 reg;
+
+ pr_debug("%s: cluster %u\n", __func__, cluster);
+ if (cluster >= SUNXI_NR_CLUSTERS)
+ return -EINVAL;
+
+ /* assert ACINACTM */
+ reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
+ reg |= CPUCFG_CX_CTRL_REG1_ACINACTM;
+ writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
+
+ /* assert cluster processor power-on resets */
+ reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+ reg &= ~PRCM_CPU_PO_RST_CTRL_CORE_ALL;
+ writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+
+ /* assert cluster resets */
+ reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+ reg &= ~CPUCFG_CX_RST_CTRL_DBG_SOC_RST;
+ reg &= ~CPUCFG_CX_RST_CTRL_DBG_RST_ALL;
+ reg &= ~CPUCFG_CX_RST_CTRL_H_RST;
+ reg &= ~CPUCFG_CX_RST_CTRL_L2_RST;
+
+ /*
+ * Allwinner code also asserts resets for NEON on A15. According
+ * to ARM manuals, asserting power-on reset is sufficient.
+ */
+ if (!(of_machine_is_compatible("allwinner,sun9i-a80") &&
+ cluster == SUN9I_A80_A15_CLUSTER)) {
+ reg &= ~CPUCFG_CX_RST_CTRL_ETM_RST_ALL;
+ }
+ writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+
+ /* hold L1/L2 reset disable signals low */
+ reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));
+ if (of_machine_is_compatible("allwinner,sun9i-a80") &&
+ cluster == SUN9I_A80_A15_CLUSTER) {
+ /* Cortex-A15: hold L2RSTDISABLE low */
+ reg &= ~CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A15;
+ } else {
+ /* Cortex-A7: hold L1RSTDISABLE and L2RSTDISABLE low */
+ reg &= ~CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE_ALL;
+ reg &= ~CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A7;
+ }
+ writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));
+
+ /* clear cluster power gate */
+ reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
+ reg &= ~PRCM_PWROFF_GATING_REG_CLUSTER;
+ writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
+ udelay(20);
+
+ /* de-assert cluster resets */
+ reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+ reg |= CPUCFG_CX_RST_CTRL_DBG_SOC_RST;
+ reg |= CPUCFG_CX_RST_CTRL_H_RST;
+ reg |= CPUCFG_CX_RST_CTRL_L2_RST;
+ writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+
+ /* de-assert ACINACTM */
+ reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
+ reg &= ~CPUCFG_CX_CTRL_REG1_ACINACTM;
+ writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
+
+ return 0;
+}
+
+static void sunxi_cpu_cache_disable(void)
+{
+ /* Disable and flush the local CPU cache. */
+ v7_exit_coherency_flush(louis);
+}
+
+/*
+ * This bit is shared between the initial mcpm_sync_init call to enable
+ * CCI-400 and proper cluster cache disable before power down.
+ */
+static void sunxi_cluster_cache_disable_without_axi(void)
+{
+ if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A15) {
+ /*
+ * On the Cortex-A15 we need to disable
+ * L2 prefetching before flushing the cache.
+ */
+ asm volatile(
+ "mcr p15, 1, %0, c15, c0, 3\n"
+ "isb\n"
+ "dsb"
+ : : "r" (0x400));
+ }
+
+ /* Flush all cache levels for this cluster. */
+ v7_exit_coherency_flush(all);
+
+ /*
+ * Disable cluster-level coherency by masking
+ * incoming snoops and DVM messages:
+ */
+ cci_disable_port_by_cpu(read_cpuid_mpidr());
+}
+
+static void sunxi_cluster_cache_disable(void)
+{
+ unsigned int cluster = MPIDR_AFFINITY_LEVEL(read_cpuid_mpidr(), 1);
+ u32 reg;
+
+ pr_info("%s: cluster %u\n", __func__, cluster);
+
+ sunxi_cluster_cache_disable_without_axi();
+
+ /* last man standing, assert ACINACTM */
+ reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
+ reg |= CPUCFG_CX_CTRL_REG1_ACINACTM;
+ writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
+}
+
+static const struct mcpm_platform_ops sunxi_power_ops = {
+ .cpu_powerup = sunxi_cpu_powerup,
+ .cluster_powerup = sunxi_cluster_powerup,
+ .cpu_cache_disable = sunxi_cpu_cache_disable,
+ .cluster_cache_disable = sunxi_cluster_cache_disable,
+};
+
+/*
+ * Enable cluster-level coherency, in preparation for turning on the MMU.
+ *
+ * Also enable regional clock gating and L2 data latency settings for
+ * Cortex-A15.
+ */
+static void __naked sunxi_power_up_setup(unsigned int affinity_level)
+{
+ asm volatile (
+ "mrc p15, 0, r1, c0, c0, 0\n"
+ "movw r2, #" __stringify(ARM_CPU_PART_MASK & 0xffff) "\n"
+ "movt r2, #" __stringify(ARM_CPU_PART_MASK >> 16) "\n"
+ "and r1, r1, r2\n"
+ "movw r2, #" __stringify(ARM_CPU_PART_CORTEX_A15 & 0xffff) "\n"
+ "movt r2, #" __stringify(ARM_CPU_PART_CORTEX_A15 >> 16) "\n"
+ "cmp r1, r2\n"
+ "bne not_a15\n"
+
+ /* The following is Cortex-A15 specific */
+
+ /* L2CTRL: Enable CPU regional clock gates */
+ "mrc p15, 1, r1, c15, c0, 4\n"
+ "orr r1, r1, #(0x1<<31)\n"
+ "mcr p15, 1, r1, c15, c0, 4\n"
+
+ /* L2ACTLR */
+ "mrc p15, 1, r1, c15, c0, 0\n"
+ /* Enable L2, GIC, and Timer regional clock gates */
+ "orr r1, r1, #(0x1<<26)\n"
+ /* Disable clean/evict from being pushed to external */
+ "orr r1, r1, #(0x1<<3)\n"
+ "mcr p15, 1, r1, c15, c0, 0\n"
+
+ /* L2 data RAM latency */
+ "mrc p15, 1, r1, c9, c0, 2\n"
+ "bic r1, r1, #(0x7<<0)\n"
+ "orr r1, r1, #(0x3<<0)\n"
+ "mcr p15, 1, r1, c9, c0, 2\n"
+
+ /* End of Cortex-A15 specific setup */
+ "not_a15:\n"
+
+ "cmp r0, #1\n"
+ "bxne lr\n"
+ "b cci_enable_port_for_self"
+ );
+}
+
+static void sunxi_mcpm_setup_entry_point(void)
+{
+ __raw_writel(virt_to_phys(mcpm_entry_point),
+ prcm_base + PRCM_CPU_SOFT_ENTRY_REG);
+}
+
+static int __init sunxi_mcpm_init(void)
+{
+ struct device_node *node;
+ int ret;
+
+ if (!of_machine_is_compatible("allwinner,sun9i-a80"))
+ return -ENODEV;
+
+ if (!cci_probed())
+ return -ENODEV;
+
+ node = of_find_compatible_node(NULL, NULL,
+ "allwinner,sun9i-a80-cpucfg");
+ if (!node)
+ return -ENODEV;
+
+ cpucfg_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!cpucfg_base) {
+ pr_err("%s: failed to map CPUCFG registers\n", __func__);
+ return -ENOMEM;
+ }
+
+ node = of_find_compatible_node(NULL, NULL,
+ "allwinner,sun9i-a80-prcm");
+ if (!node)
+ return -ENODEV;
+
+ prcm_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!prcm_base) {
+ pr_err("%s: failed to map PRCM registers\n", __func__);
+ iounmap(prcm_base);
+ return -ENOMEM;
+ }
+
+ ret = mcpm_platform_register(&sunxi_power_ops);
+ if (!ret)
+ ret = mcpm_sync_init(sunxi_power_up_setup);
+ if (!ret)
+ /* do not disable AXI master as no one will re-enable it */
+ ret = mcpm_loopback(sunxi_cluster_cache_disable_without_axi);
+ if (ret) {
+ iounmap(cpucfg_base);
+ iounmap(prcm_base);
+ return ret;
+ }
+
+ mcpm_smp_set_ops();
+
+ pr_info("sunxi MCPM support installed\n");
+
+ sunxi_mcpm_setup_entry_point();
+
+ return ret;
+}
+
+early_initcall(sunxi_mcpm_init);
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 2/5] ARM: sunxi: mcpm: Add support for A83T
From: Mylène Josserand @ 2017-12-29 10:55 UTC (permalink / raw)
To: maxime.ripard, wens, linux, robh+dt, mark.rutland
Cc: devicetree, linux-arm-kernel, linux-kernel, clabbe.montjoie,
thomas.petazzoni, mylene.josserand, quentin.schulz
In-Reply-To: <20171229105506.24851-1-mylene.josserand@free-electrons.com>
Add the support for A83T.
A83T SoC has an additional register than A80 to handle CPU configurations:
R_CPUS_CFG. Information about the register comes from Allwinner's BSP
driver.
An important difference is the Power Off Gating register for clusters
which is BIT(4) in case of SUN9I-A80 and BIT(0) in case of SUN8I-A83T.
Signed-off-by: Mylène Josserand <mylene.josserand@free-electrons.com>
---
arch/arm/mach-sunxi/Kconfig | 1 +
arch/arm/mach-sunxi/mcpm.c | 142 ++++++++++++++++++++++++++++++++++++++++----
2 files changed, 133 insertions(+), 10 deletions(-)
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 177380548d99..ae7b57fbd7ac 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -42,6 +42,7 @@ config MACH_SUN8I
default ARCH_SUNXI
select ARM_GIC
select MFD_SUN6I_PRCM
+ imply MCPM
config MACH_SUN9I
bool "Allwinner (sun9i) SoCs support"
diff --git a/arch/arm/mach-sunxi/mcpm.c b/arch/arm/mach-sunxi/mcpm.c
index 4b6e1d6ae379..716a888df70e 100644
--- a/arch/arm/mach-sunxi/mcpm.c
+++ b/arch/arm/mach-sunxi/mcpm.c
@@ -43,17 +43,25 @@
#define CPUCFG_CX_RST_CTRL_L2_RST BIT(8)
#define CPUCFG_CX_RST_CTRL_CX_RST(n) BIT(4 + (n))
#define CPUCFG_CX_RST_CTRL_CORE_RST(n) BIT(n)
+#define CPUCFG_CX_RST_CTRL_CORE_RST_ALL (0xf << 0)
#define PRCM_CPU_PO_RST_CTRL(c) (0x4 + 0x4 * (c))
#define PRCM_CPU_PO_RST_CTRL_CORE(n) BIT(n)
#define PRCM_CPU_PO_RST_CTRL_CORE_ALL 0xf
#define PRCM_PWROFF_GATING_REG(c) (0x100 + 0x4 * (c))
-#define PRCM_PWROFF_GATING_REG_CLUSTER BIT(4)
+/* The power off register for clusters are different from SUN9I and SUN8I */
+#define PRCM_PWROFF_GATING_REG_CLUSTER_SUN8I BIT(0)
+#define PRCM_PWROFF_GATING_REG_CLUSTER_SUN9I BIT(4)
#define PRCM_PWROFF_GATING_REG_CORE(n) BIT(n)
#define PRCM_PWR_SWITCH_REG(c, cpu) (0x140 + 0x10 * (c) + 0x4 * (cpu))
#define PRCM_CPU_SOFT_ENTRY_REG 0x164
+#define R_CPUCFG_CLUSTER_PO_RST_CTRL(c) (0x30 + (c) * 0x4)
+#define R_CPUCFG_CLUSTER_PO_RST_CTRL_CORE(n) BIT(n)
+#define R_CPUCFG_CPU_SOFT_ENTRY_REG 0x01a4
+
static void __iomem *cpucfg_base;
+static void __iomem *r_cpucfg_base;
static void __iomem *prcm_base;
static int sunxi_cpu_power_switch_set(unsigned int cpu, unsigned int cluster,
@@ -101,6 +109,16 @@ static int sunxi_cpu_powerup(unsigned int cpu, unsigned int cluster)
reg &= ~PRCM_CPU_PO_RST_CTRL_CORE(cpu);
writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+ if (r_cpucfg_base) {
+ /* assert cpu power-on reset */
+ reg = readl(r_cpucfg_base +
+ R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
+ reg &= ~(R_CPUCFG_CLUSTER_PO_RST_CTRL_CORE(cpu));
+ writel(reg, r_cpucfg_base +
+ R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
+ udelay(10);
+ }
+
/* Cortex-A7: hold L1 reset disable signal low */
if (!(of_machine_is_compatible("allwinner,sun9i-a80") &&
cluster == SUN9I_A80_A15_CLUSTER)) {
@@ -126,17 +144,37 @@ static int sunxi_cpu_powerup(unsigned int cpu, unsigned int cluster)
/* open power switch */
sunxi_cpu_power_switch_set(cpu, cluster, true);
+ /* Handle A83T bit swap */
+ if (of_machine_is_compatible("allwinner,sun8i-a83t")) {
+ if (cpu == 0)
+ cpu = 4;
+ }
+
/* clear processor power gate */
reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
reg &= ~PRCM_PWROFF_GATING_REG_CORE(cpu);
writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
udelay(20);
+ if (of_machine_is_compatible("allwinner,sun8i-a83t")) {
+ if (cpu == 4)
+ cpu = 0;
+ }
+
/* de-assert processor power-on reset */
reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
reg |= PRCM_CPU_PO_RST_CTRL_CORE(cpu);
writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+ if (r_cpucfg_base) {
+ reg = readl(r_cpucfg_base +
+ R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
+ reg |= R_CPUCFG_CLUSTER_PO_RST_CTRL_CORE(cpu);
+ writel(reg, r_cpucfg_base +
+ R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
+ udelay(10);
+ }
+
/* de-assert all processor resets */
reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
reg |= CPUCFG_CX_RST_CTRL_DBG_RST(cpu);
@@ -160,6 +198,14 @@ static int sunxi_cluster_powerup(unsigned int cluster)
if (cluster >= SUNXI_NR_CLUSTERS)
return -EINVAL;
+ /* For A83T, assert cluster cores resets */
+ if (of_machine_is_compatible("allwinner,sun8i-a83t")) {
+ reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+ reg &= ~CPUCFG_CX_RST_CTRL_CORE_RST_ALL; /* Core Reset */
+ writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
+ udelay(10);
+ }
+
/* assert ACINACTM */
reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
reg |= CPUCFG_CX_CTRL_REG1_ACINACTM;
@@ -170,6 +216,16 @@ static int sunxi_cluster_powerup(unsigned int cluster)
reg &= ~PRCM_CPU_PO_RST_CTRL_CORE_ALL;
writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
+ /* assert cluster cores resets */
+ if (r_cpucfg_base) {
+ reg = readl(r_cpucfg_base +
+ R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
+ reg &= ~CPUCFG_CX_RST_CTRL_CORE_RST_ALL;
+ writel(reg, r_cpucfg_base +
+ R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
+ udelay(10);
+ }
+
/* assert cluster resets */
reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
reg &= ~CPUCFG_CX_RST_CTRL_DBG_SOC_RST;
@@ -202,7 +258,10 @@ static int sunxi_cluster_powerup(unsigned int cluster)
/* clear cluster power gate */
reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
- reg &= ~PRCM_PWROFF_GATING_REG_CLUSTER;
+ if (of_machine_is_compatible("allwinner,sun8i-a83t"))
+ reg &= ~PRCM_PWROFF_GATING_REG_CLUSTER_SUN8I;
+ else
+ reg &= ~PRCM_PWROFF_GATING_REG_CLUSTER_SUN9I;
writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
udelay(20);
@@ -327,23 +386,54 @@ static void __naked sunxi_power_up_setup(unsigned int affinity_level)
static void sunxi_mcpm_setup_entry_point(void)
{
- __raw_writel(virt_to_phys(mcpm_entry_point),
- prcm_base + PRCM_CPU_SOFT_ENTRY_REG);
+ if (of_machine_is_compatible("allwinner,sun9i-a80"))
+ __raw_writel(virt_to_phys(mcpm_entry_point),
+ prcm_base + PRCM_CPU_SOFT_ENTRY_REG);
+ else
+ __raw_writel(virt_to_phys(mcpm_entry_point), r_cpucfg_base +
+ R_CPUCFG_CPU_SOFT_ENTRY_REG);
}
-static int __init sunxi_mcpm_init(void)
+static int sun9i_dt_parsing(void)
{
struct device_node *node;
- int ret;
- if (!of_machine_is_compatible("allwinner,sun9i-a80"))
+ node = of_find_compatible_node(NULL, NULL,
+ "allwinner,sun9i-a80-cpucfg");
+ if (!node)
return -ENODEV;
- if (!cci_probed())
+ cpucfg_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!cpucfg_base) {
+ pr_err("%s: failed to map CPUCFG registers\n", __func__);
+ return -ENOMEM;
+ }
+
+ node = of_find_compatible_node(NULL, NULL,
+ "allwinner,sun9i-a80-prcm");
+ if (!node)
return -ENODEV;
+ prcm_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!prcm_base) {
+ pr_err("%s: failed to map PRCM registers\n", __func__);
+ iounmap(prcm_base);
+ return -ENOMEM;
+ }
+
+ r_cpucfg_base = NULL;
+
+ return 0;
+}
+
+static int sun8i_dt_parsing(void)
+{
+ struct device_node *node;
+
node = of_find_compatible_node(NULL, NULL,
- "allwinner,sun9i-a80-cpucfg");
+ "allwinner,sun8i-a83t-cpucfg");
if (!node)
return -ENODEV;
@@ -355,7 +445,7 @@ static int __init sunxi_mcpm_init(void)
}
node = of_find_compatible_node(NULL, NULL,
- "allwinner,sun9i-a80-prcm");
+ "allwinner,sun8i-a83t-prcm");
if (!node)
return -ENODEV;
@@ -367,6 +457,38 @@ static int __init sunxi_mcpm_init(void)
return -ENOMEM;
}
+ node = of_find_compatible_node(NULL, NULL,
+ "allwinner,sun8i-a83t-r-cpucfg");
+ if (!node)
+ return -ENODEV;
+
+ r_cpucfg_base = of_iomap(node, 0);
+ of_node_put(node);
+ if (!r_cpucfg_base) {
+ pr_err("%s: failed to map R-CPUCFG registers\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int __init sunxi_mcpm_init(void)
+{
+ int ret;
+
+ if (!of_machine_is_compatible("allwinner,sun9i-a80") &&
+ !of_machine_is_compatible("allwinner,sun8i-a83t"))
+ return -ENODEV;
+
+ if (!cci_probed())
+ return -ENODEV;
+
+ if (of_machine_is_compatible("allwinner,sun9i-a80"))
+ ret = sun9i_dt_parsing();
+ else
+ ret = sun8i_dt_parsing();
+
ret = mcpm_platform_register(&sunxi_power_ops);
if (!ret)
ret = mcpm_sync_init(sunxi_power_up_setup);
--
2.11.0
^ permalink raw reply related
* [PATCH v2 3/5] arm: dts: sun8i: a83t: Add registers needed for MCPM
From: Mylène Josserand @ 2017-12-29 10:55 UTC (permalink / raw)
To: maxime.ripard, wens, linux, robh+dt, mark.rutland
Cc: thomas.petazzoni, devicetree, linux-kernel, quentin.schulz,
clabbe.montjoie, mylene.josserand, linux-arm-kernel
In-Reply-To: <20171229105506.24851-1-mylene.josserand@free-electrons.com>
Add 3 registers needed for MCPM (ie SMP): prcm, cpucfg and r_cpucfg.
prcm and cpucfg are identical with sun9i-a80. The only difference
is the r_cpucfg that does not exist on sun9i.
Signed-off-by: Mylène Josserand <mylene.josserand@free-electrons.com>
---
arch/arm/boot/dts/sun8i-a83t.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index de5119a2a91c..a6c69b75fe6c 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -162,6 +162,11 @@
#size-cells = <1>;
ranges;
+ cpucfg@1700000 {
+ compatible = "allwinner,sun8i-a83t-cpucfg";
+ reg = <0x01700000 0x400>;
+ };
+
syscon: syscon@1c00000 {
compatible = "allwinner,sun8i-a83t-system-controller",
"syscon";
@@ -595,6 +600,11 @@
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
};
+ prcm@1f01400 {
+ compatible = "allwinner,sun8i-a83t-prcm";
+ reg = <0x1f01400 0x400>;
+ };
+
r_ccu: clock@1f01400 {
compatible = "allwinner,sun8i-a83t-r-ccu";
reg = <0x01f01400 0x400>;
@@ -605,6 +615,11 @@
#reset-cells = <1>;
};
+ r_cpucfg@1f01c00 {
+ compatible = "allwinner,sun8i-a83t-r-cpucfg";
+ reg = <0x1f01c00 0x100>;
+ };
+
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun8i-a83t-r-pinctrl";
reg = <0x01f02c00 0x400>;
--
2.11.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v2 4/5] arm: dts: sun8i: a83t: Add CCI-400 node
From: Mylène Josserand @ 2017-12-29 10:55 UTC (permalink / raw)
To: maxime.ripard, wens, linux, robh+dt, mark.rutland
Cc: devicetree, linux-arm-kernel, linux-kernel, clabbe.montjoie,
thomas.petazzoni, mylene.josserand, quentin.schulz
In-Reply-To: <20171229105506.24851-1-mylene.josserand@free-electrons.com>
Add CCI-400 node and control-port on CPUs needed by MCPM (ie SMP).
Signed-off-by: Mylène Josserand <mylene.josserand@free-electrons.com>
---
arch/arm/boot/dts/sun8i-a83t.dtsi | 41 +++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index a6c69b75fe6c..aa27ffb7db81 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -62,48 +62,56 @@
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
+ cci-control-port = <&cci_control0>;
};
cpu@1 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <1>;
+ cci-control-port = <&cci_control0>;
};
cpu@2 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <2>;
+ cci-control-port = <&cci_control0>;
};
cpu@3 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <3>;
+ cci-control-port = <&cci_control0>;
};
cpu@100 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0x100>;
+ cci-control-port = <&cci_control1>;
};
cpu@101 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0x101>;
+ cci-control-port = <&cci_control1>;
};
cpu@102 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0x102>;
+ cci-control-port = <&cci_control1>;
};
cpu@103 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0x103>;
+ cci-control-port = <&cci_control1>;
};
};
@@ -167,6 +175,39 @@
reg = <0x01700000 0x400>;
};
+ cci@1790000 {
+ compatible = "arm,cci-400";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x01790000 0x10000>;
+ ranges = <0x0 0x01790000 0x10000>;
+
+ cci_control0: slave-if@4000 {
+ compatible = "arm,cci-400-ctrl-if";
+ interface-type = "ace";
+ reg = <0x4000 0x1000>;
+ };
+
+ cci_control1: slave-if@5000 {
+ compatible = "arm,cci-400-ctrl-if";
+ interface-type = "ace";
+ reg = <0x5000 0x1000>;
+ };
+
+ pmu@9000 {
+ compatible = "arm,cci-400-pmu,r1";
+ reg = <0x9000 0x5000>;
+ interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 154 IRQ_TYPE_LEVEL_HIGH>;
+ };
+ };
+
syscon: syscon@1c00000 {
compatible = "allwinner,sun8i-a83t-system-controller",
"syscon";
--
2.11.0
^ permalink raw reply related
* [PATCH v2 5/5] arm: dts: sun8i: a83t: Fix undefined offset with virtual timer
From: Mylène Josserand @ 2017-12-29 10:55 UTC (permalink / raw)
To: maxime.ripard, wens, linux, robh+dt, mark.rutland
Cc: devicetree, linux-arm-kernel, linux-kernel, clabbe.montjoie,
thomas.petazzoni, mylene.josserand, quentin.schulz
In-Reply-To: <20171229105506.24851-1-mylene.josserand@free-electrons.com>
The ARM architected timers use an offset between their physical and
virtual counters. That offset should be configured by the bootloader
in CNTVOFF.
However, the A83t bootloader fails to do so, and we end up with an
undefined offset (which in our case is random), meaning that each CPU
will have a different time, which isn't working very well.
Fix that by setting the arm,cpu-registers-not-fw-configured that will
make Linux use the physical timers instead of the virtual ones. One
possible side effect would be that the virtualization features would
be disabled. However, due to the way the GIC has been integrated in
the system, it is already unusable so we're effectively not losing any
feature.
Signed-off-by: Mylène Josserand <mylene.josserand@free-electrons.com>
---
arch/arm/boot/dts/sun8i-a83t.dtsi | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index aa27ffb7db81..ddcd90ae3a73 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -121,6 +121,7 @@
<GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
+ arm,cpu-registers-not-fw-configured;
};
clocks {
--
2.11.0
^ permalink raw reply related
* Re: [PATCH 0/4] Sunxi: Add SMP support on A83T
From: Mylene JOSSERAND @ 2017-12-29 11:04 UTC (permalink / raw)
To: Corentin Labbe
Cc: Maxime Ripard, wens-jdAy2FN1RRM, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20171228203125.GB18393@Red>
Hello Corentin,
Le Thu, 28 Dec 2017 21:31:25 +0100,
Corentin Labbe <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> a écrit :
[...]
> Hello
>
> With the .config that you give me in private, everything seems to work.
> But with mine, the stacktrace still happen.
> After some research, this is due to the following code:
> cpumask_set_cpu(get_cpu(), &cci_pmu->cpus);
> which disable preemption (via get_cpu())
>
> So it is unrelated with your patch, I will send a bug report tomorow.
Okay, great to know that it is not related to my patches.
>
> Furthermore, you can add:
> Tested-by: Corentin Labbe <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Thanks, I have just sent a V2. I did not add your tested-by because
I have done some modifications. Could you test this new series and give
me your tested-by again? It would be great!
Thank you in advance,
Best regards,
Mylène
--
Mylène Josserand, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 0/5] Sunxi: Add SMP support on A83T
From: Mylene JOSSERAND @ 2017-12-29 11:24 UTC (permalink / raw)
To: maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8, wens-jdAy2FN1RRM,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
In-Reply-To: <20171229105506.24851-1-mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Hello,
Le Fri, 29 Dec 2017 11:55:01 +0100,
Mylène Josserand <mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> a écrit :
> Hello everyone,
>
> This is a V2 of my series that adds SMP support for Allwinner sun8i-a83t
> with MCPM (Multi-Cluster Power Management).
> Based on last linux-next (next-20171222).
>
> Changes since v1:
> - Add Chen Yu's patch in my series (see path 01)
> - Add new compatibles for prcm and cpucfg registers for sun8i-a83t.
> Create two functions to separate the DT parsing of sun9i-a80 and
> sun8i-a83t.
> - Thanks to Maxime's review: order device tree's nodes according
> to physical addresses, remove unused label and fix registers' sizes.
> Update the commit log and commit title of my last patch (see
> patch 05).
>
> Patch 01: Patch from Chen Yu Tsai that adds SMP support on A80
> with MCPM
> Patch 02: Convert the MCPM driver to use it for A83T. This SoC has a
> bit flip that needs to be handled.
> Patch 03: Add registers nodes (prcm, cpucfg and r_cpucfg) needed
> for MCPM.
> Patch 04: Add CCI-400 node for a83t.
> Patch 05: Fix the use of virtual timers that hangs the kernel in
> case of SMP support.
>
> If you have any remarks/questions, let me know.
> Thank you in advance,
> Mylène
>
> Chen-Yu Tsai (1):
> ARM: sun9i: Support SMP on A80 with Multi-Cluster Power Management
> (MCPM)
Oops, I noticed that Chen Yu sent a new version of his patches on
July 2017:
https://patchwork.kernel.org/patch/9861179/
https://patchwork.kernel.org/patch/9861175/
https://patchwork.kernel.org/patch/9861173/
https://patchwork.kernel.org/patch/9861177/
I will send a V3 using this series instead of the old RFC one:
https://patchwork.kernel.org/patch/6402801/
Sorry about the noise.
Mylène
>
> Mylène Josserand (4):
> ARM: sunxi: mcpm: Add support for A83T
> arm: dts: sun8i: a83t: Add registers needed for MCPM
> arm: dts: sun8i: a83t: Add CCI-400 node
> arm: dts: sun8i: a83t: Fix undefined offset with virtual timer
>
> arch/arm/boot/dts/sun8i-a83t.dtsi | 57 +++++
> arch/arm/mach-sunxi/Kconfig | 11 +
> arch/arm/mach-sunxi/Makefile | 1 +
> arch/arm/mach-sunxi/mcpm.c | 513 ++++++++++++++++++++++++++++++++++++++
> 4 files changed, 582 insertions(+)
> create mode 100644 arch/arm/mach-sunxi/mcpm.c
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 1/2] nvmem: add driver for JZ4780 efuse
From: Marcin Nowakowski @ 2017-12-29 12:35 UTC (permalink / raw)
To: Mathieu Malaterre
Cc: Greg Kroah-Hartman, Zubair.Kakakhel, PrasannaKumar Muralidharan,
Srinivas Kandagatla, Rob Herring, Mark Rutland, Ralf Baechle,
linux-kernel, devicetree, linux-mips
In-Reply-To: <20171228212954.2922-2-malat@debian.org>
Hi Mathieu,
On 28.12.2017 22:29, Mathieu Malaterre wrote:
> --- /dev/null
> +++ b/drivers/nvmem/jz4780-efuse.c
> @@ -0,0 +1,305 @@
> +/*
> + * JZ4780 EFUSE Memory Support driver
> + *
> + * Copyright (c) 2017 PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + */
> +
Can you use SPDX identifier instead?
> +/*
> + * Currently supports JZ4780 efuse which has 8K programmable bit.
> + * Efuse is separated into seven segments as below:
> + *
> + * -----------------------------------------------------------------------
> + * | 64 bit | 128 bit | 128 bit | 3520 bit | 8 bit | 2296 bit | 2048 bit |
> + * -----------------------------------------------------------------------
> + *
> + * The rom itself is accessed using a 9 bit address line and an 8 word wide bus
> + * which reads/writes based on strobes. The strobe is configured in the config
> + * register and is based on number of cycles of the bus clock.
> + *
> + * Driver supports read only as the writes are done in the Factory.
> + */
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +
> +#define JZ_EFUCTRL (0x0) /* Control Register */
> +#define JZ_EFUCFG (0x4) /* Configure Register*/
> +#define JZ_EFUSTATE (0x8) /* Status Register */
> +#define JZ_EFUDATA(n) (0xC + (n)*4)
> +
> +#define JZ_EFUSE_START_ADDR 0x200
> +#define JZ_EFUSE_SEG1_OFF 0x00 /* 64 bit Random Number */
> +#define JZ_EFUSE_SEG2_OFF 0x08 /* 128 bit Ingenic Chip ID */
> +#define JZ_EFUSE_SEG3_OFF 0x18 /* 128 bit Customer ID */
> +#define JZ_EFUSE_SEG4_OFF 0x28 /* 3520 bit Reserved */
> +#define JZ_EFUSE_SEG5_OFF 0x1E0 /* 8 bit Protect Segment */
> +#define JZ_EFUSE_SEG6_OFF 0x1E1 /* 2296 bit HDMI Key */
> +#define JZ_EFUSE_SEG7_OFF 0x300 /* 2048 bit Security boot key */
> +#define JZ_EFUSE_END_ADDR 0x5FF
> +
> +#define JZ_EFUSE_EFUCTRL_CS BIT(30)
> +#define JZ_EFUSE_EFUCTRL_ADDR_MASK 0x1FF
> +#define JZ_EFUSE_EFUCTRL_ADDR_SHIFT 21
> +#define JZ_EFUSE_EFUCTRL_LEN_MASK 0x1F
> +#define JZ_EFUSE_EFUCTRL_LEN_SHIFT 16
> +#define JZ_EFUSE_EFUCTRL_PG_EN BIT(15)
> +#define JZ_EFUSE_EFUCTRL_WR_EN BIT(1)
> +#define JZ_EFUSE_EFUCTRL_RD_EN BIT(0)
> +
> +#define JZ_EFUSE_EFUCFG_INT_EN BIT(31)
> +#define JZ_EFUSE_EFUCFG_RD_ADJ_MASK 0xF
> +#define JZ_EFUSE_EFUCFG_RD_ADJ_SHIFT 20
> +#define JZ_EFUSE_EFUCFG_RD_STR_MASK 0xF
> +#define JZ_EFUSE_EFUCFG_RD_STR_SHIFT 16
> +#define JZ_EFUSE_EFUCFG_WR_ADJ_MASK 0xF
> +#define JZ_EFUSE_EFUCFG_WR_ADJ_SHIFT 12
> +#define JZ_EFUSE_EFUCFG_WR_STR_MASK 0xFFF
> +#define JZ_EFUSE_EFUCFG_WR_STR_SHIFT 0
> +
> +#define JZ_EFUSE_EFUSTATE_WR_DONE BIT(1)
> +#define JZ_EFUSE_EFUSTATE_RD_DONE BIT(0)
> +
> +#define JZ_EFUSE_WORD_SIZE 16
> +#define JZ_EFUSE_STRIDE 8
> +
> +struct jz4780_efuse {
> + struct device *dev;
> + void __iomem *iomem;
> + struct clk *clk;
> + unsigned int rd_adj;
> + unsigned int rd_strobe;
> +};
> +
> +/* We read 32 byte chunks to avoid complexity in the driver. */
> +static int jz4780_efuse_read_32bytes(struct jz4780_efuse *efuse, char *buf,
> + unsigned int addr)
> +{
> + unsigned int tmp = 0;
> + int i = 0;
> + int timeout = 1000;
> + int size = 32;
> +
> + /* 1. Set config register */
> + tmp = readl(efuse->iomem + JZ_EFUCFG);
> + tmp &= ~((JZ_EFUSE_EFUCFG_RD_ADJ_MASK << JZ_EFUSE_EFUCFG_RD_ADJ_SHIFT)
> + | (JZ_EFUSE_EFUCFG_RD_STR_MASK << JZ_EFUSE_EFUCFG_RD_STR_SHIFT));
> + tmp |= (efuse->rd_adj << JZ_EFUSE_EFUCFG_RD_ADJ_SHIFT)
> + | (efuse->rd_strobe << JZ_EFUSE_EFUCFG_RD_STR_SHIFT);
> + writel(tmp, efuse->iomem + JZ_EFUCFG);
> +
> + /*
> + * 2. Set control register to indicate what to read data address,
> + * read data numbers and read enable.
> + */
> + tmp = readl(efuse->iomem + JZ_EFUCTRL);
> + tmp &= ~(JZ_EFUSE_EFUCFG_RD_STR_SHIFT
> + | (JZ_EFUSE_EFUCTRL_ADDR_MASK << JZ_EFUSE_EFUCTRL_ADDR_SHIFT)
> + | JZ_EFUSE_EFUCTRL_PG_EN | JZ_EFUSE_EFUCTRL_WR_EN
> + | JZ_EFUSE_EFUCTRL_WR_EN);
> +
> + /* Need to select CS bit if address accesses upper 4Kbits memory */
> + if (addr >= (JZ_EFUSE_START_ADDR + 512))
> + tmp |= JZ_EFUSE_EFUCTRL_CS;
> +
> + tmp |= (addr << JZ_EFUSE_EFUCTRL_ADDR_SHIFT)
> + | ((size - 1) << JZ_EFUSE_EFUCTRL_LEN_SHIFT)
> + | JZ_EFUSE_EFUCTRL_RD_EN;
> + writel(tmp, efuse->iomem + JZ_EFUCTRL);
> +
> + /*
> + * 3. Wait status register RD_DONE set to 1 or EFUSE interrupted,
> + * software can read EFUSE data buffer 0 - 8 registers.
> + */
> + do {
> + tmp = readl(efuse->iomem + JZ_EFUSTATE);
> + usleep_range(1000, 2000);
> + if (timeout--)
> + break;
> + } while (!(tmp & JZ_EFUSE_EFUSTATE_RD_DONE));
> +
> + if (timeout <= 0) {
> + dev_err(efuse->dev, "Timed out while reading\n");
> + return -EAGAIN;
> + }
> +
> + for (i = 0; i < (size / 4); i++)
> + *((unsigned int *)(buf + i * 4))
> + = readl(efuse->iomem + JZ_EFUDATA(i));
> +
> + return 0;
> +}
> +
> +static unsigned int segments[][2] = {
const?
> + /* offset , size in bytes */
> + { JZ_EFUSE_SEG1_OFF, 64 >> 3 }, /* bit Random Number */
> + { JZ_EFUSE_SEG2_OFF, 128 >> 3 }, /* bit Ingenic Chip ID */
> + { JZ_EFUSE_SEG3_OFF, 128 >> 3 }, /* bit Customer ID */
> + { JZ_EFUSE_SEG4_OFF, 3520 >> 3 }, /* bit Reserved */
> + { JZ_EFUSE_SEG5_OFF, 8 >> 3 }, /* bit Protect Segment */
> + { JZ_EFUSE_SEG6_OFF, 2296 >> 3 }, /* bit HDMI Key */
> + { JZ_EFUSE_SEG7_OFF, 2048 >> 3 } /* bit Security boot key */
> +};
> +
> +#define MAX(x, y) (((x) > (y)) ? (x) : (y))
> +#define MIN(x, y) (((x) < (y)) ? (x) : (y))
> +
> +/* PM recommends read/write each segment separately */
> +static int jz4780_efuse_read_segment(struct jz4780_efuse *efuse, int segid,
> + unsigned int *offset, char *out, size_t *bytes)
> +{
> + char buf[32];
> + unsigned int lpos, buflen, ncount, remain;
> + unsigned int *segment = segments[segid];
> + int j;
> + char *cur = out;
> + int ret;
> +
> + if (*bytes == 0 ||
> + (*offset < segment[0] || *offset >= segment[0] + segment[1])) {
> + // nothing to see, move along
> + return 0;
> + }
> + lpos = MAX(segment[0], *offset);
> + buflen = MIN(segment[1], *bytes);
if *offset > segment[0] then you may read past the current segment.
On the other hand some segments are smaller than 32 bytes, so when
jz4780_efuse_read_32bytes() is used, there will often be accesses across
segment boundaries.
For this reason I don't see much point in having this split for segment
reads. If it is really recommended/required (I haven't read the SoC's
PM) then the read_32bytes() method needs to be changed to allow reads of
any length (which would allow simplifying this method a lot).
Alternatively if there isn't really such requirement, you could just
read the whole memory without worrying about segment boundaries.
> + ncount = buflen / 32;
> + remain = buflen % 32;
> +
> + for (j = 0; j < ncount ; ++j) {
> + ret = jz4780_efuse_read_32bytes(efuse, buf, lpos);
> + if (ret < 0)
> + return ret;
> +
> + memcpy(cur, buf, sizeof(buf));
> + cur += sizeof(buf);
> + lpos += sizeof(buf);
> + }
> + if (remain) {
> + ret = jz4780_efuse_read_32bytes(efuse, buf, lpos);
> + if (ret < 0)
> + return ret;
> +
> + memcpy(cur, buf, remain);
> + cur += remain;
> + }
> + *offset += buflen;
> + *bytes -= buflen;
> + return buflen;
> +}
> +
> +/* main entry point */
> +static int jz4780_efuse_read(void *context, unsigned int offset,
> + void *val, size_t bytes)
> +{
> + static const int nsegments = sizeof(segments) / sizeof(*segments);
any particular reason nsegments is static?
> + struct jz4780_efuse *efuse = context;
> + char *cur = val;
> + int i, ret;
> +
> + for (i = 0; i < nsegments; ++i) {
> + ret = jz4780_efuse_read_segment(efuse, i, &offset, cur, &bytes);
> + if (ret < 0)
> + return ret;
> + cur += ret;
> + }
> +
> + return 0;
> +}
> +
> +static struct nvmem_config jz4780_efuse_nvmem_config = {
> + .name = "jz4780-efuse",
> + .read_only = true,
> + .word_size = JZ_EFUSE_WORD_SIZE,
> + .stride = JZ_EFUSE_STRIDE,
> + .owner = THIS_MODULE,
> + .reg_read = jz4780_efuse_read,
> +};
> +
> +static int jz4780_efuse_probe(struct platform_device *pdev)
> +{
> + struct nvmem_device *nvmem;
> + struct jz4780_efuse *efuse;
> + struct resource *res;
> + unsigned long clk_rate;
> + struct device *dev = &pdev->dev;
> +
> + efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);
> + if (!efuse)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + efuse->iomem = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> + if (IS_ERR(efuse->iomem))
> + return PTR_ERR(efuse->iomem);
> +
> + efuse->clk = devm_clk_get(&pdev->dev, "bus_clk");
> + if (IS_ERR(efuse->clk))
> + return PTR_ERR(efuse->clk);
> +
> + clk_rate = clk_get_rate(efuse->clk);
> + /*
> + * rd_adj and rd_strobe are 4 bit values
> + * bus clk period * (rd_adj + 1) > 6.5ns
> + * bus clk period * (rd_adj + 5 + rd_strobe) > 35ns
> + */
> + efuse->rd_adj = (((6500 * (clk_rate / 1000000)) / 1000000) + 1) - 1;
> + efuse->rd_strobe = ((((35000 * (clk_rate / 1000000)) / 1000000) + 1)
> + - 5 - efuse->rd_adj);
> +
> + if ((efuse->rd_adj > 0x1F) || (efuse->rd_strobe > 0x1F)) {
> + dev_err(&pdev->dev, "Cannot set clock configuration\n");
> + return -EINVAL;
> + }
> + efuse->dev = dev;
> +
> + jz4780_efuse_nvmem_config.size = 1024;
> + jz4780_efuse_nvmem_config.dev = &pdev->dev;
> + jz4780_efuse_nvmem_config.priv = efuse;
> +
> + nvmem = nvmem_register(&jz4780_efuse_nvmem_config);
> + if (IS_ERR(nvmem))
> + return PTR_ERR(nvmem);
> +
> + platform_set_drvdata(pdev, nvmem);
> +
> + return 0;
> +}
> +
> +static int jz4780_efuse_remove(struct platform_device *pdev)
> +{
> + struct nvmem_device *nvmem = platform_get_drvdata(pdev);
> +
> + return nvmem_unregister(nvmem);
> +}
> +
> +static const struct of_device_id jz4780_efuse_match[] = {
> + { .compatible = "ingenic,jz4780-efuse" },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, jz4780_efuse_match);
> +
> +static struct platform_driver jz4780_efuse_driver = {
> + .probe = jz4780_efuse_probe,
> + .remove = jz4780_efuse_remove,
> + .driver = {
> + .name = "jz4780-efuse",
> + .of_match_table = jz4780_efuse_match,
> + },
> +};
> +module_platform_driver(jz4780_efuse_driver);
> +
> +MODULE_AUTHOR("PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>");
> +MODULE_DESCRIPTION("Ingenic JZ4780 efuse driver");
> +MODULE_LICENSE("GPL v2");
>
^ permalink raw reply
* Re: [PATCH v5 05/16] media: rkisp1: add Rockchip ISP1 subdev driver
From: Philippe Ombredanne @ 2017-12-29 12:45 UTC (permalink / raw)
To: Shunqian Zheng
Cc: open list:ARM/Rockchip SoC..., Linux Media Mailing List, LKML,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Mauro Carvalho Chehab, Sakari Ailus, Hans Verkuil, Tomasz Figa,
Laurent Pinchart, zyc-TNX95d0MmH7DzftRWevZcw,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w, Jeffy Chen,
allon.huang-TNX95d0MmH7DzftRWevZcw,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Heiko Stuebner, Rob Herring, Joao Pinto <Joa>
In-Reply-To: <1514533978-20408-6-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Shunqian,
On Fri, Dec 29, 2017 at 8:52 AM, Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
> From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> Add the subdev driver for rockchip isp1.
<snip>
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/rkisp1.c
> @@ -0,0 +1,1205 @@
> +/*
> + * Rockchip isp1 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + *
> + * This software is available to you under a choice of one of two
> + * licenses. You may choose to be licensed under the terms of the GNU
> + * General Public License (GPL) Version 2, available from the file
> + * COPYING in the main directory of this source tree, or the
> + * OpenIB.org BSD license below:
> + *
> + * Redistribution and use in source and binary forms, with or
> + * without modification, are permitted provided that the following
> + * conditions are met:
> + *
> + * - Redistributions of source code must retain the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer.
> + *
> + * - Redistributions in binary form must reproduce the above
> + * copyright notice, this list of conditions and the following
> + * disclaimer in the documentation and/or other materials
> + * provided with the distribution.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
> + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
> + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> + * SOFTWARE.
> + */
Do you mind using a simpler SPDX identifier instead of this long
legalese boilerplate?
This is documented in Thomas doc patches. This applies to your entire
patch set of course.
Thanks!
--
Cordially
Philippe Ombredanne
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 6/9] v4l: i2c: Copy ov772x soc_camera sensor driver
From: Philippe Ombredanne @ 2017-12-29 12:47 UTC (permalink / raw)
To: Jacopo Mondi
Cc: Laurent Pinchart, Magnus Damm, geert, Mauro Carvalho Chehab,
Hans Verkuil, Rob Herring, Mark Rutland, linux-renesas-soc,
Linux Media Mailing List, linux-sh,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML
In-Reply-To: <1514469681-15602-7-git-send-email-jacopo+renesas@jmondi.org>
Jacopo,
On Thu, Dec 28, 2017 at 3:01 PM, Jacopo Mondi <jacopo+renesas@jmondi.org> wrote:
> Copy the soc_camera based driver in v4l2 sensor driver directory.
> This commit just copies the original file without modifying it.
> No modification to KConfig and Makefile as soc_camera framework
> dependencies need to be removed first in next commit.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> drivers/media/i2c/ov772x.c | 1124 ++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 1124 insertions(+)
> create mode 100644 drivers/media/i2c/ov772x.c
>
> diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
> new file mode 100644
> index 0000000..8063835
> --- /dev/null
> +++ b/drivers/media/i2c/ov772x.c
> @@ -0,0 +1,1124 @@
> +/*
> + * ov772x Camera Driver
> + *
> + * Copyright (C) 2008 Renesas Solutions Corp.
> + * Kuninori Morimoto <morimoto.kuninori@renesas.com>
> + *
> + * Based on ov7670 and soc_camera_platform driver,
> + *
> + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
> + * Copyright (C) 2008 Magnus Damm
> + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
Do you mind using a simpler SPDX identifier instead of this long
legalese boilerplate?
This is documented in Thomas doc patches. This applies to your entire
patch set of course.
Thanks!
--
Cordially
Philippe Ombredanne
^ permalink raw reply
* Re: [PATCH v4 06/15] clk: Add Ingenic jz4770 CGU driver
From: Philippe Ombredanne @ 2017-12-29 12:55 UTC (permalink / raw)
To: Paul Cercueil
Cc: Ralf Baechle, Rob Herring, Michael Turquette, Mark Rutland,
Stephen Boyd, Maarten ter Huurne,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML,
Linux MIPS, linux-clk-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171228135634.30000-7-paul-icTtO2rgO2OTuSrc4Mpeew@public.gmane.org>
Dear Mr Crapouillou-Cercueil-Sir,
On Thu, Dec 28, 2017 at 2:56 PM, Paul Cercueil <paul-icTtO2rgO2OTuSrc4Mpeew@public.gmane.org> wrote:
> Add support for the clocks provided by the CGU in the Ingenic JZ4770
> SoC.
<snip>
> --- /dev/null
> +++ b/drivers/clk/ingenic/jz4770-cgu.c
> @@ -0,0 +1,487 @@
> +/*
> + * JZ4770 SoC CGU driver
> + *
> + * Copyright 2017, Paul Cercueil <paul-icTtO2rgO2OTuSrc4Mpeew@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 or later
> + * as published by the Free Software Foundation.
> + */
Do you mind using a simpler one-line SPDX identifier instead of this
fine but clearly crapouillish legalese boilerplate? Unless you are
trying to turn the kernel in a legal compendium, of course ;)
This is documented in Thomas doc patches. This would apply to your
entire patch set.
Thank you for your kind consideration!
--
Cordially
Philippe Ombredanne
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox