From ef5cb53961177db6a6bf85c99c9c99f70052f27c Mon Sep 17 00:00:00 2001 From: Raphael <68374617+raphaelscholle@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:49:49 +0200 Subject: [PATCH] patches --- scripts/patches/openhd_cams.patch | 4167 ++++++++++++++++++++++++ scripts/patches/openhd_cm3.patch | 21 + scripts/patches/openhd_drm-rock5.patch | 31 + scripts/patches/openhd_i2c.patch | 38 + scripts/patches/openhd_keyboard.patch | 1329 ++++++++ scripts/patches/openhd_uart.patch | 74 + scripts/patches/openhd_utils.patch | 49 + scripts/patches/openhd_wifi.patch | 68 + 8 files changed, 5777 insertions(+) create mode 100644 scripts/patches/openhd_cams.patch create mode 100644 scripts/patches/openhd_cm3.patch create mode 100644 scripts/patches/openhd_drm-rock5.patch create mode 100644 scripts/patches/openhd_i2c.patch create mode 100644 scripts/patches/openhd_keyboard.patch create mode 100644 scripts/patches/openhd_uart.patch create mode 100644 scripts/patches/openhd_utils.patch create mode 100644 scripts/patches/openhd_wifi.patch diff --git a/scripts/patches/openhd_cams.patch b/scripts/patches/openhd_cams.patch new file mode 100644 index 0000000000000..bf23018031584 --- /dev/null +++ b/scripts/patches/openhd_cams.patch @@ -0,0 +1,4167 @@ +commit 155b58c3ee6813253d8fc9b3662af1532acf1030 +Author: Raphael <68374617+raphaelscholle@users.noreply.github.com> +Date: Sat Aug 24 00:30:22 2024 +0200 + + Enable OpenHD Cams on Rock5 + +diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig +index 7569882f3474..3f1c9c7d3b79 100644 +--- a/arch/arm64/configs/rockchip_linux_defconfig ++++ b/arch/arm64/configs/rockchip_linux_defconfig +@@ -833,6 +833,9 @@ CONFIG_VIDEO_GC2093=y + CONFIG_VIDEO_GC8034=y + CONFIG_VIDEO_IMX219=y + CONFIG_VIDEO_IMX415=y ++CONFIG_VIDEO_ARDUCAM_PIVARIETY=m ++CONFIG_VIDEO_IMX708=m ++CONFIG_VIDEO_IMX462=m + CONFIG_VIDEO_IMX464=y + CONFIG_VIDEO_OS04A10=y + CONFIG_VIDEO_OV4689=y +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index d66d1b1f2999..f13baa4c4289 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -1300,6 +1300,36 @@ config VIDEO_IMX415 + To compile this driver as a module, choose M here: the + module will be called imx415. + ++config VIDEO_ARDUCAM_PIVARIETY ++ tristate "Arducam pivariety camera support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ help ++ This is a Video4Linux2 sensor driver for the Arducam ++ pivariety camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called arducam_pivariety. ++ ++config VIDEO_IMX462 ++ tristate "Arducam 462 sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ help ++ This is a Video4Linux2 sensor driver for the Arducam ++ imx462 camera. ++ ++config VIDEO_IMX708 ++ tristate "Arducam IMX708 sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ help ++ This is a Video4Linux2 sensor driver for the Arducam ++ pivariety camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called imx708. ++ + config VIDEO_IMX464 + tristate "Sony IMX464 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index eb2cad76f4fa..313b84f51120 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -35,6 +35,9 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o + obj-$(CONFIG_VIDEO_DW9800W) += dw9800w.o + obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o + obj-$(CONFIG_VIDEO_FP5510) += fp5510.o ++obj-$(CONFIG_VIDEO_ARDUCAM_PIVARIETY) += arducam-pivariety.o ++obj-$(CONFIG_VIDEO_IMX462) += imx462.o ++obj-$(CONFIG_VIDEO_IMX708) += imx708.o + obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o + obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o + obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o +diff --git a/drivers/media/i2c/arducam-pivariety.c b/drivers/media/i2c/arducam-pivariety.c +new file mode 100644 +index 000000000000..0daf0b53b77b +--- /dev/null ++++ b/drivers/media/i2c/arducam-pivariety.c +@@ -0,0 +1,1746 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * A V4L2 driver for Arducam Pivariety Cameras ++ * Copyright (C) 2022 Arducam Technology co., Ltd. ++ * ++ * Based on Sony IMX219 camera driver ++ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd ++ * ++ * I2C read and write method is taken from the OV9281 driver ++ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../platform/rockchip/isp/rkisp_tb_helper.h" ++#include "arducam-pivariety.h" ++#include "../platform/rockchip/isp/rkisp_tb_helper.h" ++ ++static int debug = 0; ++module_param(debug, int, 0644); ++ ++#define PIVARIETY_NAME "pivariety" ++#define DRIVER_VERSION KERNEL_VERSION(0, 0x00, 0x10) ++ ++/* regulator supplies */ ++static const char * const pivariety_supply_name[] = { ++ /* Supplies can be enabled in any order */ ++ "VANA", /* Analog (2.8V) supply */ ++ "VDIG", /* Digital Core (1.8V) supply */ ++ "VDDL", /* IF (1.2V) supply */ ++}; ++ ++/* The supported raw formats. */ ++static const u32 codes[] = { ++ MEDIA_BUS_FMT_SBGGR8_1X8, ++ MEDIA_BUS_FMT_SGBRG8_1X8, ++ MEDIA_BUS_FMT_SGRBG8_1X8, ++ MEDIA_BUS_FMT_SRGGB8_1X8, ++ MEDIA_BUS_FMT_Y8_1X8, ++ ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_Y10_1X10, ++ ++ MEDIA_BUS_FMT_SBGGR12_1X12, ++ MEDIA_BUS_FMT_SGBRG12_1X12, ++ MEDIA_BUS_FMT_SGRBG12_1X12, ++ MEDIA_BUS_FMT_SRGGB12_1X12, ++ MEDIA_BUS_FMT_Y12_1X12, ++}; ++ ++#define ARDUCAM_NUM_SUPPLIES ARRAY_SIZE(pivariety_supply_name) ++ ++#define ARDUCAM_XCLR_MIN_DELAY_US 10000 ++#define ARDUCAM_XCLR_DELAY_RANGE_US 1000 ++ ++#define MAX_CTRLS 32 ++ ++struct pivariety { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ ++ //struct v4l2_mbus_config_mipi_csi2 bus; ++ struct clk *xclk; ++ u32 xclk_freq; ++ ++ struct gpio_desc *reset_gpio; ++ struct regulator_bulk_data supplies[ARDUCAM_NUM_SUPPLIES]; ++ ++ struct arducam_format *supported_formats; ++ int num_supported_formats; ++ int current_format_idx; ++ int current_resolution_idx; ++ int lanes; ++ int bayer_order_volatile; ++ bool wait_until_free; ++ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *ctrls[MAX_CTRLS]; ++ /* V4L2 Controls */ ++ struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *hflip; ++ ++ struct v4l2_rect crop; ++ /* ++ * Mutex for serialized access: ++ * Protect sensor module set pad format and start/stop streaming safely. ++ */ ++ struct mutex mutex; ++ ++ /* Streaming on/off */ ++ bool streaming; ++ bool power_on; ++ bool is_thunderboot; ++ bool is_thunderboot_ng; ++ ++ u32 module_index; ++ const char *module_facing; ++ const char *module_name; ++ const char *len_name; ++}; ++ ++static const s64 link_freq_menu_items[] = { ++ 456000000, ++}; ++ ++static inline struct pivariety *to_pivariety(struct v4l2_subdev *_sd) ++{ ++ return container_of(_sd, struct pivariety, sd); ++} ++ ++/* Write registers up to 4 at a time */ ++static int pivariety_write_reg(struct i2c_client *client, u16 reg, u32 val) ++{ ++ unsigned int len = sizeof(u32); ++ u32 buf_i, val_i = 0; ++ u8 buf[6]; ++ u8 *val_p; ++ __be32 val_be; ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ ++ val_be = cpu_to_be32(val); ++ val_p = (u8 *)&val_be; ++ buf_i = 2; ++ ++ 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; ++} ++ ++/* Read registers up to 4 at a time */ ++static int pivariety_read_reg(struct i2c_client *client, u16 reg, u32 *val) ++{ ++ struct i2c_msg msgs[2]; ++ unsigned int len = sizeof(u32); ++ u8 *data_be_p; ++ __be32 data_be = 0; ++ __be16 reg_addr_be = cpu_to_be16(reg); ++ int ret; ++ ++ 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; ++ ++ 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 ++pivariety_read(struct pivariety *pivariety, u16 addr, u32 *value) ++{ ++ struct v4l2_subdev *sd = &pivariety->sd; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret, count = 0; ++ ++ while (count++ < I2C_READ_RETRY_COUNT) { ++ ret = pivariety_read_reg(client, addr, value); ++ if (!ret) { ++ v4l2_dbg(2, debug, sd, "%s: 0x%02x 0x%04x\n", ++ __func__, addr, *value); ++ return ret; ++ } ++ } ++ ++ v4l2_err(sd, "%s: Reading register 0x%02x failed\n", ++ __func__, addr); ++ ++ return ret; ++} ++ ++static int pivariety_write(struct pivariety *pivariety, u16 addr, u32 value) ++{ ++ struct v4l2_subdev *sd = &pivariety->sd; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret, count = 0; ++ ++ while (count++ < I2C_WRITE_RETRY_COUNT) { ++ ret = pivariety_write_reg(client, addr, value); ++ if (!ret) ++ return ret; ++ } ++ ++ v4l2_err(sd, "%s: Write 0x%04x to register 0x%02x failed\n", ++ __func__, value, addr); ++ ++ return ret; ++} ++ ++static int wait_for_free(struct pivariety *pivariety, int interval) ++{ ++ u32 value; ++ u32 count = 0; ++ ++ while (count++ < (1000 / interval)) { ++ int ret = pivariety_read(pivariety, SYSTEM_IDLE_REG, &value); ++ ++ if (!ret && !value) ++ break; ++ msleep(interval); ++ } ++ ++ v4l2_dbg(2, debug, &pivariety->sd, "%s: End wait, Count: %d.\n", ++ __func__, count); ++ ++ return 0; ++} ++ ++static int is_raw(int pixformat) ++{ ++ return pixformat >= 0x28 && pixformat <= 0x2D; ++} ++ ++static u32 bayer_to_mbus_code(int data_type, int bayer_order) ++{ ++ const u32 depth8[] = { ++ MEDIA_BUS_FMT_SBGGR8_1X8, ++ MEDIA_BUS_FMT_SGBRG8_1X8, ++ MEDIA_BUS_FMT_SGRBG8_1X8, ++ MEDIA_BUS_FMT_SRGGB8_1X8, ++ MEDIA_BUS_FMT_Y8_1X8, ++ }; ++ ++ const u32 depth10[] = { ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_Y10_1X10, ++ }; ++ ++ const u32 depth12[] = { ++ MEDIA_BUS_FMT_SBGGR12_1X12, ++ MEDIA_BUS_FMT_SGBRG12_1X12, ++ MEDIA_BUS_FMT_SGRBG12_1X12, ++ MEDIA_BUS_FMT_SRGGB12_1X12, ++ MEDIA_BUS_FMT_Y12_1X12, ++ }; ++ ++ if (bayer_order < 0 || bayer_order > 4) ++ return 0; ++ ++ switch (data_type) { ++ case IMAGE_DT_RAW8: ++ return depth8[bayer_order]; ++ case IMAGE_DT_RAW10: ++ return depth10[bayer_order]; ++ case IMAGE_DT_RAW12: ++ return depth12[bayer_order]; ++ } ++ ++ return 0; ++} ++ ++static u32 yuv422_to_mbus_code(int data_type, int order) ++{ ++ const u32 depth8[] = { ++ MEDIA_BUS_FMT_YUYV8_1X16, ++ MEDIA_BUS_FMT_YVYU8_1X16, ++ MEDIA_BUS_FMT_UYVY8_1X16, ++ MEDIA_BUS_FMT_VYUY8_1X16, ++ }; ++ ++ const u32 depth10[] = { ++ MEDIA_BUS_FMT_YUYV10_1X20, ++ MEDIA_BUS_FMT_YVYU10_1X20, ++ MEDIA_BUS_FMT_UYVY10_1X20, ++ MEDIA_BUS_FMT_VYUY10_1X20, ++ }; ++ ++ if (order < 0 || order > 3) ++ return 0; ++ ++ switch (data_type) { ++ case IMAGE_DT_YUV422_8: ++ return depth8[order]; ++ case IMAGE_DT_YUV422_10: ++ return depth10[order]; ++ } ++ ++ return 0; ++} ++ ++static u32 data_type_to_mbus_code(int data_type, int bayer_order) ++{ ++ if (is_raw(data_type)) ++ return bayer_to_mbus_code(data_type, bayer_order); ++ ++ switch (data_type) { ++ case IMAGE_DT_YUV422_8: ++ case IMAGE_DT_YUV422_10: ++ return yuv422_to_mbus_code(data_type, bayer_order); ++ case IMAGE_DT_RGB565: ++ return MEDIA_BUS_FMT_RGB565_2X8_LE; ++ case IMAGE_DT_RGB888: ++ return MEDIA_BUS_FMT_RGB888_1X24; ++ } ++ ++ return 0; ++} ++ ++/* Get bayer order based on flip setting. */ ++static u32 pivariety_get_format_code(struct pivariety *pivariety, ++ struct arducam_format *format) ++{ ++ unsigned int order, origin_order; ++ ++ lockdep_assert_held(&pivariety->mutex); ++ ++ /* ++ * Only the bayer format needs to transform the format. ++ */ ++ if (!is_raw(format->data_type) || ++ !pivariety->bayer_order_volatile || ++ format->bayer_order == BAYER_ORDER_GRAY) ++ return data_type_to_mbus_code(format->data_type, ++ format->bayer_order); ++ ++ order = format->bayer_order; ++ ++ origin_order = order; ++ ++ order = (pivariety->hflip && pivariety->hflip->val ? order ^ 1 : order); ++ order = (pivariety->vflip && pivariety->vflip->val ? order ^ 2 : order); ++ ++ v4l2_dbg(1, debug, &pivariety->sd, "%s: before: %d, after: %d.\n", ++ __func__, origin_order, order); ++ ++ return data_type_to_mbus_code(format->data_type, order); ++} ++ ++/* Power/clock management functions */ ++static int pivariety_power_on(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct pivariety *pivariety = to_pivariety(sd); ++ int ret; ++ ++ ret = regulator_bulk_enable(ARDUCAM_NUM_SUPPLIES, ++ pivariety->supplies); ++ if (ret) { ++ dev_err(dev, "%s: failed to enable regulators\n", ++ __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(pivariety->xclk); ++ if (ret) { ++ dev_err(dev, "%s: failed to enable clock\n", ++ __func__); ++ goto reg_off; ++ } ++ ++ gpiod_set_value_cansleep(pivariety->reset_gpio, 1); ++ usleep_range(ARDUCAM_XCLR_MIN_DELAY_US, ++ ARDUCAM_XCLR_MIN_DELAY_US + ARDUCAM_XCLR_DELAY_RANGE_US); ++ ++ return 0; ++ ++reg_off: ++ regulator_bulk_disable(ARDUCAM_NUM_SUPPLIES, pivariety->supplies); ++ ++ return ret; ++} ++ ++static int pivariety_power_off(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct pivariety *pivariety = to_pivariety(sd); ++ ++ gpiod_set_value_cansleep(pivariety->reset_gpio, 0); ++ regulator_bulk_disable(ARDUCAM_NUM_SUPPLIES, pivariety->supplies); ++ clk_disable_unprepare(pivariety->xclk); ++ ++ return 0; ++} ++ ++static int pivariety_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct v4l2_mbus_framefmt *try_fmt = ++ v4l2_subdev_get_try_format(sd, fh->pad, 0); ++ struct arducam_format *def_fmt = &pivariety->supported_formats[0]; ++ ++ /* Initialize try_fmt */ ++ try_fmt->width = def_fmt->resolution_set->width; ++ try_fmt->height = def_fmt->resolution_set->height; ++ try_fmt->code = def_fmt->mbus_code; ++ try_fmt->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int pivariety_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ int ret, i; ++ struct pivariety *pivariety = ++ container_of(ctrl->handler, struct pivariety, ++ ctrl_handler); ++ struct arducam_format *supported_fmts = pivariety->supported_formats; ++ int num_supported_formats = pivariety->num_supported_formats; ++ ++ v4l2_dbg(3, debug, &pivariety->sd, "%s: cid = (0x%X), value = (%d).\n", ++ __func__, ctrl->id, ctrl->val); ++ ++ ret = pivariety_write(pivariety, CTRL_ID_REG, ctrl->id); ++ ret += pivariety_write(pivariety, CTRL_VALUE_REG, ctrl->val); ++ if (ret < 0) ++ return -EINVAL; ++ ++ /* When flip is set, modify all bayer formats */ ++ if (ctrl->id == V4L2_CID_VFLIP || ctrl->id == V4L2_CID_HFLIP) { ++ for (i = 0; i < num_supported_formats; i++) { ++ supported_fmts[i].mbus_code = ++ pivariety_get_format_code(pivariety, ++ &supported_fmts[i]); ++ } ++ } ++ ++ /* ++ * When starting streaming, controls are set in batches, ++ * and the short interval will cause some controls to be unsuccessfully ++ * set. ++ */ ++ if (pivariety->wait_until_free) ++ wait_for_free(pivariety, 1); ++ else ++ usleep_range(200, 210); ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops pivariety_ctrl_ops = { ++ .s_ctrl = pivariety_s_ctrl, ++}; ++ ++static int pivariety_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct arducam_format *supported_formats = pivariety->supported_formats; ++ int num_supported_formats = pivariety->num_supported_formats; ++ ++ v4l2_dbg(1, debug, sd, "%s: index = (%d)\n", __func__, code->index); ++ ++ if (code->index >= num_supported_formats) ++ return -EINVAL; ++ ++ code->code = supported_formats[code->index].mbus_code; ++ ++ return 0; ++} ++ ++static int pivariety_enum_framesizes(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ int i; ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct arducam_format *supported_formats = pivariety->supported_formats; ++ int num_supported_formats = pivariety->num_supported_formats; ++ struct arducam_format *format; ++ struct arducam_resolution *resolution; ++ ++ v4l2_dbg(1, debug, sd, "%s: code = (0x%X), index = (%d)\n", ++ __func__, fse->code, fse->index); ++ ++ for (i = 0; i < num_supported_formats; i++) { ++ format = &supported_formats[i]; ++ if (fse->code == format->mbus_code) { ++ if (fse->index >= format->num_resolution_set) ++ return -EINVAL; ++ ++ resolution = &format->resolution_set[fse->index]; ++ fse->min_width = resolution->width; ++ fse->max_width = resolution->width; ++ fse->min_height = resolution->height; ++ fse->max_height = resolution->height; ++ ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int pivariety_enum_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_interval_enum *fie) ++{ ++ int i; ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct arducam_format *supported_formats = pivariety->supported_formats; ++ int num_supported_formats = pivariety->num_supported_formats; ++ struct arducam_format *format; ++ struct arducam_resolution *resolution; ++ ++ struct v4l2_fract tmp = { ++ .numerator = 10000, ++ .denominator = 600000, ++ }; ++ ++ v4l2_dbg(1, debug, sd, "%s: code = (0x%X), index = (%d)\n", ++ __func__, fie->code, fie->index); ++ ++ for (i = 0; i < num_supported_formats; i++) { ++ format = &supported_formats[i]; ++ if (fie->code == format->mbus_code) { ++ // if (fie->index >= format->num_resolution_set) ++ // return -EINVAL; ++ fie->code = MEDIA_BUS_FMT_SRGGB10_1X10; ++ resolution = &format->resolution_set[fie->index]; ++ fie->width = resolution->width; ++ fie->height = resolution->height; ++ fie->interval = tmp; ++ fie->reserved[0] = NO_HDR; ++ ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int pivariety_get_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct arducam_format *current_format = ++ &pivariety->supported_formats[pivariety->current_format_idx]; ++ struct v4l2_mbus_framefmt *fmt = &format->format; ++ int cur_res_idx = pivariety->current_resolution_idx; ++ ++ // if (format->pad != 0) ++ // return -EINVAL; ++ ++ // mutex_lock(&pivariety->mutex); ++ ++ // current_format = ++ // &pivariety->supported_formats[pivariety->current_format_idx]; ++ // cur_res_idx = pivariety->current_resolution_idx; ++ format->format.width = ++ current_format->resolution_set[cur_res_idx].width; ++ format->format.height = ++ current_format->resolution_set[cur_res_idx].height; ++ format->format.code = current_format->mbus_code; ++ format->format.field = V4L2_FIELD_NONE; ++ fmt->colorspace = V4L2_COLORSPACE_RAW; ++ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); ++ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, ++ fmt->colorspace, ++ fmt->ycbcr_enc); ++ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); ++ ++ v4l2_dbg(1, debug, sd, "%s: width: (%d) height: (%d) code: (0x%X)\n", ++ __func__, format->format.width, format->format.height, ++ format->format.code); ++ ++ // mutex_unlock(&pivariety->mutex); ++ return 0; ++} ++ ++static int pivariety_get_fmt_idx_by_code(struct pivariety *pivariety, ++ u32 mbus_code) ++{ ++ int i; ++ u32 data_type; ++ struct arducam_format *formats = pivariety->supported_formats; ++ ++ for (i = 0; i < pivariety->num_supported_formats; i++) { ++ if (formats[i].mbus_code == mbus_code) ++ return i; ++ } ++ ++ /* ++ * If the specified format is not found in the list of supported ++ * formats, try to find a format of the same data type. ++ */ ++ for (i = 0; i < ARRAY_SIZE(codes); i++) ++ if (codes[i] == mbus_code) ++ break; ++ ++ if (i >= ARRAY_SIZE(codes)) ++ return -EINVAL; ++ ++ data_type = i / 5 + IMAGE_DT_RAW8; ++ ++ for (i = 0; i < pivariety->num_supported_formats; i++) { ++ if (formats[i].data_type == data_type) ++ return i; ++ } ++ ++ return -EINVAL; ++} ++ ++static struct v4l2_ctrl *get_control(struct pivariety *pivariety, ++ u32 id) ++{ ++ int index = 0; ++ ++ while (index < MAX_CTRLS && pivariety->ctrls[index]) { ++ if (pivariety->ctrls[index]->id == id) ++ return pivariety->ctrls[index]; ++ index++; ++ } ++ ++ return NULL; ++} ++ ++static int update_control(struct pivariety *pivariety, u32 id) ++{ ++ struct v4l2_subdev *sd = &pivariety->sd; ++ struct v4l2_ctrl *ctrl; ++ u32 min, max, step, def, id2; ++ int ret = 0; ++ ++ pivariety_write(pivariety, CTRL_ID_REG, id); ++ pivariety_read(pivariety, CTRL_ID_REG, &id2); ++ ++ v4l2_dbg(1, debug, sd, "%s: Write ID: 0x%08X Read ID: 0x%08X\n", ++ __func__, id, id2); ++ ++ pivariety_write(pivariety, CTRL_VALUE_REG, 0); ++ wait_for_free(pivariety, 1); ++ ++ ret += pivariety_read(pivariety, CTRL_MAX_REG, &max); ++ ret += pivariety_read(pivariety, CTRL_MIN_REG, &min); ++ ret += pivariety_read(pivariety, CTRL_DEF_REG, &def); ++ ret += pivariety_read(pivariety, CTRL_STEP_REG, &step); ++ ++ if (ret < 0) ++ goto err; ++ ++ if (id == NO_DATA_AVAILABLE || max == NO_DATA_AVAILABLE || ++ min == NO_DATA_AVAILABLE || def == NO_DATA_AVAILABLE || ++ step == NO_DATA_AVAILABLE) ++ goto err; ++ ++ v4l2_dbg(1, debug, sd, "%s: min: %d, max: %d, step: %d, def: %d\n", ++ __func__, min, max, step, def); ++ ++ ctrl = get_control(pivariety, id); ++ return __v4l2_ctrl_modify_range(ctrl, min, max, step, def); ++ ++err: ++ return -EINVAL; ++} ++ ++static int update_controls(struct pivariety *pivariety) ++{ ++ int ret = 0; ++ int index = 0; ++ ++ wait_for_free(pivariety, 5); ++ ++ while (index < MAX_CTRLS && pivariety->ctrls[index]) { ++ ret += update_control(pivariety, pivariety->ctrls[index]->id); ++ index++; ++ } ++ ++ return ret; ++} ++ ++static int pivariety_set_fmt(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *format) ++{ ++ int i, j; ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct arducam_format *supported_formats = pivariety->supported_formats; ++ ++ if (format->pad != 0) ++ return -EINVAL; ++ ++ mutex_lock(&pivariety->mutex); ++ ++ format->format.colorspace = V4L2_COLORSPACE_RAW; ++ format->format.field = V4L2_FIELD_NONE; ++ ++ v4l2_dbg(1, debug, sd, "%s: code: 0x%X, width: %d, height: %d\n", ++ __func__, format->format.code, format->format.width, ++ format->format.height); ++ ++ i = pivariety_get_fmt_idx_by_code(pivariety, format->format.code); ++ if (i < 0) ++ i = 0; ++ ++ format->format.code = supported_formats[i].mbus_code; ++ ++ for (j = 0; j < supported_formats[i].num_resolution_set; j++) { ++ if (supported_formats[i].resolution_set[j].width == ++ format->format.width && ++ supported_formats[i].resolution_set[j].height == ++ format->format.height) { ++ v4l2_dbg(1, debug, sd, ++ "%s: format match.\n", __func__); ++ v4l2_dbg(1, debug, sd, ++ "%s: set format to device: %d %d.\n", ++ __func__, supported_formats[i].index, j); ++ ++ pivariety_write(pivariety, PIXFORMAT_INDEX_REG, ++ supported_formats[i].index); ++ pivariety_write(pivariety, RESOLUTION_INDEX_REG, j); ++ ++ pivariety->current_format_idx = i; ++ pivariety->current_resolution_idx = j; ++ ++ update_controls(pivariety); ++ ++ goto unlock; ++ } ++ } ++ ++ format->format.width = supported_formats[i].resolution_set[0].width; ++ format->format.height = supported_formats[i].resolution_set[0].height; ++ ++ pivariety_write(pivariety, PIXFORMAT_INDEX_REG, ++ supported_formats[i].index); ++ pivariety_write(pivariety, RESOLUTION_INDEX_REG, 0); ++ ++ pivariety->current_format_idx = i; ++ pivariety->current_resolution_idx = 0; ++ update_controls(pivariety); ++ ++unlock: ++ ++ mutex_unlock(&pivariety->mutex); ++ ++ return 0; ++} ++ ++static int pivariety_g_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ // struct pivariety *pivariety = to_pivariety(sd); ++ ++ struct v4l2_fract tmp = { ++ .numerator = 10000, ++ .denominator = 300000, ++ }; ++ // mutex_lock(&pivariety->mutex); ++ fi->interval = tmp; ++ // mutex_unlock(&pivariety->mutex); ++ ++ return 0; ++} ++ ++/* Start streaming */ ++static int pivariety_start_streaming(struct pivariety *pivariety) ++{ ++ int ret; ++ ++ /* set stream on register */ ++ ret = pivariety_write(pivariety, MODE_SELECT_REG, ++ ARDUCAM_MODE_STREAMING); ++ ++ if (ret) ++ return ret; ++ ++ wait_for_free(pivariety, 2); ++ ++ /* ++ * When starting streaming, controls are set in batches, ++ * and the short interval will cause some controls to be unsuccessfully ++ * set. ++ */ ++ pivariety->wait_until_free = true; ++ /* Apply customized values from user */ ++ ret = __v4l2_ctrl_handler_setup(pivariety->sd.ctrl_handler); ++ ++ pivariety->wait_until_free = false; ++ if (ret) ++ return ret; ++ ++ wait_for_free(pivariety, 2); ++ ++ return ret; ++} ++ ++static int pivariety_read_sel(struct pivariety *pivariety, ++ struct v4l2_rect *rect) ++{ ++ int ret = 0; ++ ++ ret += pivariety_read(pivariety, IPC_SEL_TOP_REG, &rect->top); ++ ret += pivariety_read(pivariety, IPC_SEL_LEFT_REG, &rect->left); ++ ret += pivariety_read(pivariety, IPC_SEL_WIDTH_REG, &rect->width); ++ ret += pivariety_read(pivariety, IPC_SEL_HEIGHT_REG, &rect->height); ++ ++ if (ret || rect->top == NO_DATA_AVAILABLE || ++ rect->left == NO_DATA_AVAILABLE || ++ rect->width == NO_DATA_AVAILABLE || ++ rect->height == NO_DATA_AVAILABLE) { ++ v4l2_err(&pivariety->sd, "%s: Failed to read selection.\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_rect * ++__pivariety_get_pad_crop(struct pivariety *pivariety, ++ struct v4l2_subdev_pad_config *cfg, ++ unsigned int pad, ++ enum v4l2_subdev_format_whence which) ++{ ++ int ret; ++ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(&pivariety->sd, cfg, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ ret = pivariety_read_sel(pivariety, &pivariety->crop); ++ if (ret) ++ return NULL; ++ return &pivariety->crop; ++ } ++ ++ return NULL; ++} ++ ++static int pivariety_get_selection(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_selection *sel) ++{ ++ int ret = 0; ++ struct v4l2_rect rect; ++ struct pivariety *pivariety = to_pivariety(sd); ++ ++ ret = pivariety_write(pivariety, IPC_SEL_TARGET_REG, sel->target); ++ if (ret) { ++ v4l2_err(sd, "%s: Write register 0x%02x failed\n", ++ __func__, IPC_SEL_TARGET_REG); ++ return -EINVAL; ++ } ++ ++ wait_for_free(pivariety, 2); ++ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: { ++ mutex_lock(&pivariety->mutex); ++ sel->r = *__pivariety_get_pad_crop(pivariety, cfg, ++ sel->pad, ++ sel->which); ++ mutex_unlock(&pivariety->mutex); ++ ++ return 0; ++ } ++ ++ case V4L2_SEL_TGT_NATIVE_SIZE: ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ ret = pivariety_read_sel(pivariety, &rect); ++ if (ret) ++ return -EINVAL; ++ ++ sel->r = rect; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/* Stop streaming */ ++static int pivariety_stop_streaming(struct pivariety *pivariety) ++{ ++ int ret; ++ ++ /* set stream off register */ ++ ret = pivariety_write(pivariety, MODE_SELECT_REG, ARDUCAM_MODE_STANDBY); ++ if (ret) ++ v4l2_err(&pivariety->sd, "%s failed to set stream\n", __func__); ++ ++ /* ++ * Return success even if it was an error, as there is nothing the ++ * caller can do about it. ++ */ ++ return 0; ++} ++ ++static int pivariety_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ mutex_lock(&pivariety->mutex); ++ ++ if (enable) { ++ ret = pm_runtime_get_sync(&client->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(&client->dev); ++ goto err_unlock; ++ } ++ ++ /* ++ * Apply default & customized values ++ * and then start streaming. ++ */ ++ ret = pivariety_start_streaming(pivariety); ++ ++ if (ret) ++ goto err_rpm_put; ++ } else { ++ pivariety_stop_streaming(pivariety); ++ ++ pm_runtime_put(&client->dev); ++ } ++ ++ pivariety->streaming = enable; ++ ++ /* ++ * vflip and hflip cannot change during streaming ++ * Pivariety may not implement flip control. ++ */ ++ if (pivariety->vflip) ++ __v4l2_ctrl_grab(pivariety->vflip, enable); ++ ++ if (pivariety->hflip) ++ __v4l2_ctrl_grab(pivariety->hflip, enable); ++ ++ mutex_unlock(&pivariety->mutex); ++ ++ return ret; ++ ++err_rpm_put: ++ pm_runtime_put(&client->dev); ++err_unlock: ++ mutex_unlock(&pivariety->mutex); ++ ++ return ret; ++} ++ ++static int __maybe_unused pivariety_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct pivariety *pivariety = to_pivariety(sd); ++ ++ if (pivariety->streaming) ++ pivariety_stop_streaming(pivariety); ++ ++ return 0; ++} ++ ++static int __maybe_unused pivariety_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct pivariety *pivariety = to_pivariety(sd); ++ int ret; ++ ++ if (pivariety->streaming) { ++ ret = pivariety_start_streaming(pivariety); ++ if (ret) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ pivariety_stop_streaming(pivariety); ++ pivariety->streaming = 0; ++ return ret; ++} ++ ++static int pivariety_get_regulators(struct pivariety *pivariety) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); ++ int i; ++ ++ for (i = 0; i < ARDUCAM_NUM_SUPPLIES; i++) ++ pivariety->supplies[i].supply = pivariety_supply_name[i]; ++ ++ return devm_regulator_bulk_get(&client->dev, ++ ARDUCAM_NUM_SUPPLIES, ++ pivariety->supplies); ++} ++ ++static int pivariety_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct pivariety *pivariety = to_pivariety(sd); ++ u32 val = 0; ++ ++ // if (pivariety->lanes > pivariety->bus.num_data_lanes) ++ // return -EINVAL; ++ ++ val = 1 << (pivariety->lanes - 1) | ++ V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ val |= V4L2_MBUS_CSI2_CHANNEL_1; ++ cfg->type = V4L2_MBUS_CSI2_DPHY; ++ cfg->flags = val; ++ // cfg->bus.mipi_csi2.flags = pivariety->bus.flags; ++ // cfg->bus.mipi_csi2.num_data_lanes = pivariety->lanes; ++ ++ return 0; ++} ++ ++static void pivariety_get_module_inf(struct pivariety *pivariety, ++ struct rkmodule_inf *inf) ++{ ++ // struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); ++ ++ memset(inf, 0, sizeof(*inf)); ++ strlcpy(inf->base.sensor, PIVARIETY_NAME, sizeof(inf->base.sensor)); ++ strlcpy(inf->base.module, pivariety->module_name, ++ sizeof(inf->base.module)); ++ strlcpy(inf->base.lens, pivariety->len_name, sizeof(inf->base.lens)); ++ ++ // v4l2_dbg(1, debug, pivariety->sd,"%s: get_module_inf:%s, %s, %s.\n", __func__, ++ // inf->base.sensor, inf->base.module, inf->base.lens); ++} ++ ++static int pivariety_get_channel_info(struct pivariety *pivariety, struct rkmodule_channel_info *ch_info) ++{ ++ struct arducam_format *current_format; ++ int cur_res_idx; ++ ++ if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) ++ return -EINVAL; ++ ++ current_format = ++ &pivariety->supported_formats[pivariety->current_format_idx]; ++ cur_res_idx = pivariety->current_resolution_idx; ++ ch_info->width = current_format->resolution_set[cur_res_idx].width; ++ ch_info->height = ++ current_format->resolution_set[cur_res_idx].height; ++ ch_info->bus_fmt = current_format->mbus_code; ++ ch_info->vc = V4L2_MBUS_CSI2_CHANNEL_0; ++ // pivariety->cur_mode->vc[ch_info->index]; ++ ++ return 0; ++} ++ ++static long pivariety_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct pivariety *pivariety = to_pivariety(sd); ++ struct rkmodule_channel_info *ch_info; ++ long ret = 0; ++ switch (cmd) ++ { ++ case RKMODULE_GET_MODULE_INFO: ++ pivariety_get_module_inf(pivariety, (struct rkmodule_inf *)arg); ++ break; ++ case RKMODULE_GET_CHANNEL_INFO: ++ ch_info = (struct rkmodule_channel_info *)arg; ++ ret = pivariety_get_channel_info(pivariety, ch_info); ++ break; ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ ++ return ret; ++} ++ ++#ifdef CONFIG_COMPAT ++static long pivariety_compat_ioctl32(struct v4l2_subdev *sd, ++ unsigned int cmd, unsigned long arg) ++{ ++ void __user *up = compat_ptr(arg); ++ struct rkmodule_inf *inf; ++ struct rkmodule_awb_cfg *cfg; ++ struct rkmodule_channel_info *ch_info; ++ long ret; ++ ++ switch (cmd) ++ { ++ case RKMODULE_GET_MODULE_INFO: ++ inf = kzalloc(sizeof(*inf), GFP_KERNEL); ++ if (!inf) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ ret = pivariety_ioctl(sd, cmd, inf); ++ if(!ret) { ++ if (copy_to_user(up, inf, sizeof(*inf))) { ++ kfree (inf); ++ return -EFAULT; ++ } ++ } ++ kfree(inf); ++ break; ++ case RKMODULE_AWB_CFG: ++ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); ++ if (!cfg) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ if (copy_from_user(cfg, up, sizeof(*cfg))) { ++ kfree(cfg); ++ return -EFAULT; ++ } ++ ret = pivariety_ioctl(sd, cmd, cfg); ++ kfree(cfg); ++ break; ++ case RKMODULE_GET_CHANNEL_INFO: ++ ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); ++ if (!ch_info) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ ret = pivariety_ioctl(sd, cmd, ch_info); ++ if (!ret) { ++ ret = copy_to_user(up, ch_info, sizeof(*ch_info)); ++ if (ret) ++ ret = -EFAULT; ++ } ++ kfree(ch_info); ++ break; ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ return ret; ++} ++#endif ++ ++static const struct v4l2_subdev_core_ops pivariety_core_ops = { ++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event, ++ // .unsubscribe_event = v4l2_event_subdev_unsubscribe, ++ .ioctl = pivariety_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl32 = pivariety_compat_ioctl32, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops pivariety_video_ops = { ++ .s_stream = pivariety_set_stream, ++ .g_frame_interval = pivariety_g_frame_interval, ++}; ++ ++static const struct v4l2_subdev_pad_ops pivariety_pad_ops = { ++ .enum_mbus_code = pivariety_enum_mbus_code, ++ .get_fmt = pivariety_get_fmt, ++ .set_fmt = pivariety_set_fmt, ++ .enum_frame_interval = pivariety_enum_frame_interval, ++ .enum_frame_size = pivariety_enum_framesizes, ++ .get_selection = pivariety_get_selection, ++ .get_mbus_config = pivariety_get_mbus_config, ++}; ++ ++static const struct v4l2_subdev_ops pivariety_subdev_ops = { ++ .core = &pivariety_core_ops, ++ .video = &pivariety_video_ops, ++ .pad = &pivariety_pad_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops pivariety_internal_ops = { ++ .open = pivariety_open, ++}; ++ ++static void pivariety_free_controls(struct pivariety *pivariety) ++{ ++ v4l2_ctrl_handler_free(pivariety->sd.ctrl_handler); ++ mutex_destroy(&pivariety->mutex); ++} ++ ++static int pivariety_get_length_of_set(struct pivariety *pivariety, ++ u16 idx_reg, u16 val_reg) ++{ ++ int ret; ++ int index = 0; ++ u32 val; ++ ++ while (1) { ++ ret = pivariety_write(pivariety, idx_reg, index); ++ ret += pivariety_read(pivariety, val_reg, &val); ++ ++ if (ret < 0) ++ return -1; ++ ++ if (val == NO_DATA_AVAILABLE) ++ break; ++ index++; ++ } ++ pivariety_write(pivariety, idx_reg, 0); ++ return index; ++} ++ ++static int pivariety_enum_resolution(struct pivariety *pivariety, ++ struct arducam_format *format) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); ++ int index = 0; ++ u32 width, height; ++ int num_resolution = 0; ++ int ret; ++ ++ num_resolution = pivariety_get_length_of_set(pivariety, ++ RESOLUTION_INDEX_REG, ++ FORMAT_WIDTH_REG); ++ if (num_resolution < 0) ++ goto err; ++ ++ format->resolution_set = devm_kzalloc(&client->dev, ++ sizeof(*format->resolution_set) * ++ num_resolution, ++ GFP_KERNEL); ++ while (1) { ++ ret = pivariety_write(pivariety, RESOLUTION_INDEX_REG, index); ++ ret += pivariety_read(pivariety, FORMAT_WIDTH_REG, &width); ++ ret += pivariety_read(pivariety, FORMAT_HEIGHT_REG, &height); ++ ++ if (ret < 0) ++ goto err; ++ ++ if (width == NO_DATA_AVAILABLE || height == NO_DATA_AVAILABLE) ++ break; ++ ++ format->resolution_set[index].width = width; ++ format->resolution_set[index].height = height; ++ ++ index++; ++ } ++ ++ format->num_resolution_set = index; ++ pivariety_write(pivariety, RESOLUTION_INDEX_REG, 0); ++ return 0; ++err: ++ return -ENODEV; ++} ++ ++static int pivariety_enum_pixformat(struct pivariety *pivariety) ++{ ++ int ret = 0; ++ u32 mbus_code = 0; ++ int pixfmt_type; ++ int bayer_order; ++ int bayer_order_not_volatile; ++ int lanes; ++ int index = 0; ++ int num_pixformat = 0; ++ struct arducam_format *arducam_fmt; ++ struct i2c_client *client = v4l2_get_subdevdata(&pivariety->sd); ++ ++ num_pixformat = pivariety_get_length_of_set(pivariety, ++ PIXFORMAT_INDEX_REG, ++ PIXFORMAT_TYPE_REG); ++ ++ if (num_pixformat < 0) ++ goto err; ++ ++ ret = pivariety_read(pivariety, FLIPS_DONT_CHANGE_ORDER_REG, ++ &bayer_order_not_volatile); ++ if (bayer_order_not_volatile == NO_DATA_AVAILABLE) ++ pivariety->bayer_order_volatile = 1; ++ else ++ pivariety->bayer_order_volatile = !bayer_order_not_volatile; ++ ++ if (ret < 0) ++ goto err; ++ ++ pivariety->supported_formats = ++ devm_kzalloc(&client->dev, ++ sizeof(*pivariety->supported_formats) * ++ num_pixformat, ++ GFP_KERNEL); ++ ++ while (1) { ++ ret = pivariety_write(pivariety, PIXFORMAT_INDEX_REG, index); ++ ret += pivariety_read(pivariety, PIXFORMAT_TYPE_REG, ++ &pixfmt_type); ++ ++ if (pixfmt_type == NO_DATA_AVAILABLE) ++ break; ++ ++ ret += pivariety_read(pivariety, MIPI_LANES_REG, &lanes); ++ if (lanes == NO_DATA_AVAILABLE) ++ break; ++ ++ ret += pivariety_read(pivariety, PIXFORMAT_ORDER_REG, ++ &bayer_order); ++ if (ret < 0) ++ goto err; ++ ++ mbus_code = data_type_to_mbus_code(pixfmt_type, bayer_order); ++ arducam_fmt = &pivariety->supported_formats[index]; ++ arducam_fmt->index = index; ++ arducam_fmt->mbus_code = mbus_code; ++ arducam_fmt->bayer_order = bayer_order; ++ arducam_fmt->data_type = pixfmt_type; ++ if (pivariety_enum_resolution(pivariety, arducam_fmt)) ++ goto err; ++ ++ index++; ++ } ++ ++ pivariety_write(pivariety, PIXFORMAT_INDEX_REG, 0); ++ pivariety->num_supported_formats = index; ++ pivariety->current_format_idx = 0; ++ pivariety->current_resolution_idx = 0; ++ pivariety->lanes = lanes; ++ ++ return 0; ++ ++err: ++ return -ENODEV; ++} ++ ++static const char *pivariety_ctrl_get_name(u32 id) ++{ ++ switch (id) { ++ case V4L2_CID_ARDUCAM_EXT_TRI: ++ return "trigger_mode"; ++ case V4L2_CID_ARDUCAM_IRCUT: ++ return "ircut"; ++ case V4L2_CID_ARDUCAM_STROBE_SHIFT: ++ return "strobe_shift"; ++ case V4L2_CID_ARDUCAM_STROBE_WIDTH: ++ return "strobe_width"; ++ case V4L2_CID_ARDUCAM_MODE: ++ return "mode"; ++ default: ++ return NULL; ++ } ++} ++ ++enum v4l2_ctrl_type pivariety_get_v4l2_ctrl_type(u32 id) ++{ ++ switch (id) { ++ case V4L2_CID_ARDUCAM_EXT_TRI: ++ return V4L2_CTRL_TYPE_BOOLEAN; ++ case V4L2_CID_ARDUCAM_IRCUT: ++ return V4L2_CTRL_TYPE_BOOLEAN; ++ default: ++ return V4L2_CTRL_TYPE_INTEGER; ++ } ++} ++ ++static struct v4l2_ctrl *v4l2_ctrl_new_arducam(struct v4l2_ctrl_handler *hdl, ++ const struct v4l2_ctrl_ops *ops, ++ u32 id, s64 min, s64 max, ++ u64 step, s64 def) ++{ ++ struct v4l2_ctrl_config ctrl_cfg = { ++ .ops = ops, ++ .id = id, ++ .name = NULL, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .flags = 0, ++ .min = min, ++ .max = max, ++ .def = def, ++ .step = step, ++ }; ++ ++ ctrl_cfg.name = pivariety_ctrl_get_name(id); ++ ctrl_cfg.type = pivariety_get_v4l2_ctrl_type(id); ++ ++ return v4l2_ctrl_new_custom(hdl, &ctrl_cfg, NULL); ++} ++ ++static int pivariety_enum_controls(struct pivariety *pivariety) ++{ ++ struct v4l2_subdev *sd = &pivariety->sd; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct v4l2_ctrl_handler *ctrl_hdlr = &pivariety->ctrl_handler; ++ struct v4l2_fwnode_device_properties props; ++ struct v4l2_ctrl **ctrl = pivariety->ctrls; ++ int ret, index, num_ctrls; ++ u32 id, min, max, def, step; ++ ++ num_ctrls = pivariety_get_length_of_set(pivariety, CTRL_INDEX_REG, ++ CTRL_ID_REG); ++ if (num_ctrls < 0) ++ goto err; ++ ++ v4l2_dbg(1, debug, sd, "%s: num_ctrls = %d\n", ++ __func__, num_ctrls); ++ ++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, num_ctrls); ++ if (ret) ++ return ret; ++ ++ index = 0; ++ while (1) { ++ ret = pivariety_write(pivariety, CTRL_INDEX_REG, index); ++ pivariety_write(pivariety, CTRL_VALUE_REG, 0); ++ wait_for_free(pivariety, 1); ++ ++ ret += pivariety_read(pivariety, CTRL_ID_REG, &id); ++ ret += pivariety_read(pivariety, CTRL_MAX_REG, &max); ++ ret += pivariety_read(pivariety, CTRL_MIN_REG, &min); ++ ret += pivariety_read(pivariety, CTRL_DEF_REG, &def); ++ ret += pivariety_read(pivariety, CTRL_STEP_REG, &step); ++ if (ret < 0) ++ goto err; ++ ++ if (id == NO_DATA_AVAILABLE || max == NO_DATA_AVAILABLE || ++ min == NO_DATA_AVAILABLE || def == NO_DATA_AVAILABLE || ++ step == NO_DATA_AVAILABLE) ++ break; ++ ++ v4l2_dbg(1, debug, sd, ++ "%s: index = %d, id = 0x%x, max = %d, min = %d, def = %d, step = %d\n", ++ __func__, index, id, max, min, def, step); ++ ++ if (v4l2_ctrl_get_name(id)) { ++ *ctrl = v4l2_ctrl_new_std(ctrl_hdlr, ++ &pivariety_ctrl_ops, ++ id, min, ++ max, step, ++ def); ++ v4l2_dbg(1, debug, sd, "%s: ctrl: 0x%p\n", ++ __func__, *ctrl); ++ } else if (pivariety_ctrl_get_name(id)) { ++ *ctrl = v4l2_ctrl_new_arducam(ctrl_hdlr, ++ &pivariety_ctrl_ops, ++ id, min, max, step, def); ++ ++ v4l2_dbg(1, debug, sd, ++ "%s: new custom ctrl, ctrl: 0x%p.\n", ++ __func__, *ctrl); ++ } else { ++ index++; ++ continue; ++ } ++ ++ if (!*ctrl) ++ goto err; ++ ++ switch (id) { ++ case V4L2_CID_HFLIP: ++ pivariety->hflip = *ctrl; ++ if (pivariety->bayer_order_volatile) ++ pivariety->hflip->flags |= ++ V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ break; ++ ++ case V4L2_CID_VFLIP: ++ pivariety->vflip = *ctrl; ++ if (pivariety->bayer_order_volatile) ++ pivariety->vflip->flags |= ++ V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ break; ++ ++ case V4L2_CID_HBLANK: ++ (*ctrl)->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ break; ++ } ++ ++ ctrl++; ++ index++; ++ } ++ /* freq */ ++ v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ, ++ 0, 0, link_freq_menu_items); ++ ++ pivariety_write(pivariety, CTRL_INDEX_REG, 0); ++ ++ ret = v4l2_fwnode_device_parse(&client->dev, &props); ++ if (ret) ++ goto err; ++ ++ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ++ &pivariety_ctrl_ops, ++ &props); ++ if (ret) ++ goto err; ++ ++ pivariety->sd.ctrl_handler = ctrl_hdlr; ++ v4l2_ctrl_handler_setup(ctrl_hdlr); ++ return 0; ++err: ++ return -ENODEV; ++} ++ ++static int pivariety_parse_dt(struct pivariety *pivariety, struct device *dev) ++{ ++ struct fwnode_handle *endpoint; ++ struct v4l2_fwnode_endpoint ep_cfg = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY ++ }; ++ int ret = -EINVAL; ++ ++ /* Get CSI2 bus config */ ++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); ++ if (!endpoint) { ++ dev_err(dev, "endpoint node not found\n"); ++ return -EINVAL; ++ } ++ ++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { ++ dev_err(dev, "could not parse endpoint\n"); ++ goto error_out; ++ } ++ ++ //pivariety->bus = ep_cfg.bus.mipi_csi2; ++ ++ ret = 0; ++ ++error_out: ++ v4l2_fwnode_endpoint_free(&ep_cfg); ++ fwnode_handle_put(endpoint); ++ ++ return ret; ++} ++ ++static int pivariety_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ struct device_node *node = dev->of_node; ++ struct pivariety *pivariety; ++ char facing[2]; ++ u32 device_id, firmware_version; ++ u32 sensor_id; ++ int ret; ++ ++ dev_info(dev, "driver version: %02x.%02x.%02x", ++ DRIVER_VERSION >> 16, ++ (DRIVER_VERSION & 0xff00) >> 8, ++ DRIVER_VERSION & 0x00ff); ++ ++ pivariety = devm_kzalloc(&client->dev, sizeof(*pivariety), GFP_KERNEL); ++ if (!pivariety) ++ return -ENOMEM; ++ ++ ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, ++ &pivariety->module_index); ++ ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, ++ &pivariety->module_facing); ++ ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, ++ &pivariety->module_name); ++ ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, ++ &pivariety->len_name); ++ /* Initialize subdev */ ++ v4l2_i2c_subdev_init(&pivariety->sd, client, ++ &pivariety_subdev_ops); ++ ++ if (pivariety_parse_dt(pivariety, dev)) ++ return -EINVAL; ++ ++ /* Get system clock (xclk) */ ++ pivariety->xclk = devm_clk_get(dev, "xclk"); ++ if (IS_ERR(pivariety->xclk)) { ++ dev_err(dev, "failed to get xclk\n"); ++ return PTR_ERR(pivariety->xclk); ++ } ++ ++ pivariety->xclk_freq = clk_get_rate(pivariety->xclk); ++ if (pivariety->xclk_freq != 24000000) { ++ dev_err(dev, "xclk frequency not supported: %d Hz\n", ++ pivariety->xclk_freq); ++ return -EINVAL; ++ } ++ ++ ret = pivariety_get_regulators(pivariety); ++ if (ret) ++ return ret; ++ ++ /* Request optional enable pin */ ++ pivariety->reset_gpio = devm_gpiod_get_optional(dev, "reset", ++ GPIOD_OUT_HIGH); ++ ++ ret = pivariety_power_on(dev); ++ if (ret) ++ return ret; ++ ++ ret = pivariety_read(pivariety, DEVICE_ID_REG, &device_id); ++ if (ret || device_id != DEVICE_ID) { ++ dev_err(dev, "probe failed\n"); ++ ret = -ENODEV; ++ goto error_power_off; ++ } ++ ++ ++ ret = pivariety_read(pivariety, DEVICE_VERSION_REG, &firmware_version); ++ if (ret) ++ dev_err(dev, "read firmware version failed\n"); ++ ++ dev_info(dev, "firmware version: 0x%04X\n", firmware_version); ++ ++ ret = pivariety_read(pivariety, SENSOR_ID_REG, &sensor_id); ++ if (ret) ++ dev_err(dev, "read sensor id failed\n"); ++ ++ if (pivariety_enum_pixformat(pivariety)) { ++ dev_err(dev, "enum pixformat failed.\n"); ++ ret = -ENODEV; ++ goto error_power_off; ++ } ++ ++ if (pivariety_enum_controls(pivariety)) { ++ dev_err(dev, "enum controls failed.\n"); ++ ret = -ENODEV; ++ goto error_power_off; ++ } ++#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API ++ /* Initialize subdev */ ++ pivariety->sd.internal_ops = &pivariety_internal_ops; ++ pivariety->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | ++ V4L2_SUBDEV_FL_HAS_EVENTS; ++#endif ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ /* Initialize source pad */ ++ pivariety->pad.flags = MEDIA_PAD_FL_SOURCE; ++ pivariety->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ ret = media_entity_pads_init(&pivariety->sd.entity, 1, &pivariety->pad); ++ if (ret) ++ goto error_handler_free; ++#endif ++ ++ memset(facing, 0, sizeof(facing)); ++ if (strcmp(pivariety->module_facing, "back") == 0) ++ facing[0] = 'b'; ++ else ++ facing[0] = 'f'; ++ ++ snprintf(pivariety->sd.name, sizeof(pivariety->sd.name), ++ "m%02x_%s_%s_%04x %s", pivariety->module_index, facing, ++ PIVARIETY_NAME, sensor_id, dev_name(pivariety->sd.dev)); ++ ++ ++ ret = v4l2_async_register_subdev_sensor_common(&pivariety->sd); ++ if (ret < 0) ++ goto error_media_entity; ++ ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ pm_runtime_idle(dev); ++ ++ return 0; ++ ++error_media_entity: ++ media_entity_cleanup(&pivariety->sd.entity); ++ ++error_handler_free: ++ pivariety_free_controls(pivariety); ++ ++error_power_off: ++ pivariety_power_off(dev); ++ ++ return ret; ++} ++ ++static int pivariety_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct pivariety *pivariety = to_pivariety(sd); ++ ++ v4l2_async_unregister_subdev(sd); ++ media_entity_cleanup(&sd->entity); ++ pivariety_free_controls(pivariety); ++ ++ pm_runtime_disable(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ return 0; ++} ++ ++static const struct dev_pm_ops pivariety_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(pivariety_suspend, pivariety_resume) ++ SET_RUNTIME_PM_OPS(pivariety_power_off, pivariety_power_on, NULL) ++}; ++ ++#if IS_ENABLED(CONFIG_OF) ++static const struct of_device_id arducam_pivariety_dt_ids[] = { ++ { .compatible = "arducam,arducam-pivariety" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, arducam_pivariety_dt_ids); ++#endif ++ ++// static const struct i2c_device_id arducam_pivariety_match_id[] = { ++// { "arducam,pivariety", 0 }, ++// { }, ++// }; ++ ++static struct i2c_driver arducam_pivariety_i2c_driver = { ++ .driver = { ++ .name = PIVARIETY_NAME, ++ .of_match_table = of_match_ptr(arducam_pivariety_dt_ids), ++ .pm = &pivariety_pm_ops, ++ }, ++ .probe = pivariety_probe, ++ .remove = pivariety_remove, ++ // .id_table = arducam_pivariety_match_id, ++}; ++ ++// module_i2c_driver(arducam_pivariety_i2c_driver); ++ ++static int __init sensor_mod_init(void) ++{ ++ return i2c_add_driver(&arducam_pivariety_i2c_driver); ++} ++ ++static void __exit sensor_mod_exit(void) ++{ ++ i2c_del_driver(&arducam_pivariety_i2c_driver); ++} ++ ++device_initcall_sync(sensor_mod_init); ++module_exit(sensor_mod_exit); ++ ++MODULE_AUTHOR("Lee Jackson "); ++MODULE_DESCRIPTION("Arducam Pivariety v4l2 driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/arducam-pivariety.h b/drivers/media/i2c/arducam-pivariety.h +new file mode 100644 +index 000000000000..99d5ada309e8 +--- /dev/null ++++ b/drivers/media/i2c/arducam-pivariety.h +@@ -0,0 +1,110 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef _ARDUCAM_PIVARIETY_H_ ++#define _ARDUCAM_PIVARIETY_H_ ++ ++#define DEVICE_REG_BASE 0x0100 ++#define PIXFORMAT_REG_BASE 0x0200 ++#define FORMAT_REG_BASE 0x0300 ++#define CTRL_REG_BASE 0x0400 ++#define IPC_REG_BASE 0x0600 ++ ++#define ARDUCAM_MODE_STANDBY 0x00 ++#define ARDUCAM_MODE_STREAMING 0x01 ++ ++#define MODE_SELECT_REG (DEVICE_REG_BASE | 0x0000) ++#define DEVICE_VERSION_REG (DEVICE_REG_BASE | 0x0001) ++#define SENSOR_ID_REG (DEVICE_REG_BASE | 0x0002) ++#define DEVICE_ID_REG (DEVICE_REG_BASE | 0x0003) ++#define SYSTEM_IDLE_REG (DEVICE_REG_BASE | 0x0007) ++ ++#define PIXFORMAT_INDEX_REG (PIXFORMAT_REG_BASE | 0x0000) ++#define PIXFORMAT_TYPE_REG (PIXFORMAT_REG_BASE | 0x0001) ++#define PIXFORMAT_ORDER_REG (PIXFORMAT_REG_BASE | 0x0002) ++#define MIPI_LANES_REG (PIXFORMAT_REG_BASE | 0x0003) ++#define FLIPS_DONT_CHANGE_ORDER_REG (PIXFORMAT_REG_BASE | 0x0004) ++ ++#define RESOLUTION_INDEX_REG (FORMAT_REG_BASE | 0x0000) ++#define FORMAT_WIDTH_REG (FORMAT_REG_BASE | 0x0001) ++#define FORMAT_HEIGHT_REG (FORMAT_REG_BASE | 0x0002) ++ ++#define CTRL_INDEX_REG (CTRL_REG_BASE | 0x0000) ++#define CTRL_ID_REG (CTRL_REG_BASE | 0x0001) ++#define CTRL_MIN_REG (CTRL_REG_BASE | 0x0002) ++#define CTRL_MAX_REG (CTRL_REG_BASE | 0x0003) ++#define CTRL_STEP_REG (CTRL_REG_BASE | 0x0004) ++#define CTRL_DEF_REG (CTRL_REG_BASE | 0x0005) ++#define CTRL_VALUE_REG (CTRL_REG_BASE | 0x0006) ++ ++#define IPC_SEL_TARGET_REG (IPC_REG_BASE | 0x0000) ++#define IPC_SEL_TOP_REG (IPC_REG_BASE | 0x0001) ++#define IPC_SEL_LEFT_REG (IPC_REG_BASE | 0x0002) ++#define IPC_SEL_WIDTH_REG (IPC_REG_BASE | 0x0003) ++#define IPC_SEL_HEIGHT_REG (IPC_REG_BASE | 0x0004) ++#define IPC_DELAY_REG (IPC_REG_BASE | 0x0005) ++ ++#define NO_DATA_AVAILABLE 0xFFFFFFFE ++ ++#define DEVICE_ID 0x0030 ++ ++#define I2C_READ_RETRY_COUNT 3 ++#define I2C_WRITE_RETRY_COUNT 2 ++ ++#define V4L2_CID_ARDUCAM_BASE (V4L2_CID_USER_BASE + 0x1000) ++#define V4L2_CID_ARDUCAM_EXT_TRI (V4L2_CID_ARDUCAM_BASE + 1) ++#define V4L2_CID_ARDUCAM_IRCUT (V4L2_CID_ARDUCAM_BASE + 8) ++#define V4L2_CID_ARDUCAM_STROBE_SHIFT (V4L2_CID_ARDUCAM_BASE + 14) ++#define V4L2_CID_ARDUCAM_STROBE_WIDTH (V4L2_CID_ARDUCAM_BASE + 15) ++#define V4L2_CID_ARDUCAM_MODE (V4L2_CID_ARDUCAM_BASE + 16) ++ ++enum image_dt { ++ IMAGE_DT_YUV420_8 = 0x18, ++ IMAGE_DT_YUV420_10, ++ ++ IMAGE_DT_YUV420CSPS_8 = 0x1C, ++ IMAGE_DT_YUV420CSPS_10, ++ IMAGE_DT_YUV422_8, ++ IMAGE_DT_YUV422_10, ++ IMAGE_DT_RGB444, ++ IMAGE_DT_RGB555, ++ IMAGE_DT_RGB565, ++ IMAGE_DT_RGB666, ++ IMAGE_DT_RGB888, ++ ++ IMAGE_DT_RAW6 = 0x28, ++ IMAGE_DT_RAW7, ++ IMAGE_DT_RAW8, ++ IMAGE_DT_RAW10, ++ IMAGE_DT_RAW12, ++ IMAGE_DT_RAW14, ++}; ++ ++enum bayer_order { ++ BAYER_ORDER_BGGR = 0, ++ BAYER_ORDER_GBRG = 1, ++ BAYER_ORDER_GRBG = 2, ++ BAYER_ORDER_RGGB = 3, ++ BAYER_ORDER_GRAY = 4, ++}; ++ ++enum yuv_order { ++ YUV_ORDER_YUYV = 0, ++ YUV_ORDER_YVYU = 1, ++ YUV_ORDER_UYVY = 2, ++ YUV_ORDER_VYUY = 3, ++}; ++ ++struct arducam_resolution { ++ u32 width; ++ u32 height; ++}; ++ ++struct arducam_format { ++ u32 index; ++ u32 mbus_code; ++ u32 bayer_order; ++ u32 data_type; ++ u32 num_resolution_set; ++ struct arducam_resolution *resolution_set; ++}; ++ ++#endif +diff --git a/drivers/media/i2c/imx462.c b/drivers/media/i2c/imx462.c +new file mode 100644 +index 000000000000..3c8d9fd37381 +--- /dev/null ++++ b/drivers/media/i2c/imx462.c +@@ -0,0 +1,19 @@ ++#include ++#include ++ ++MODULE_AUTHOR("OpenHD"); ++MODULE_DESCRIPTION("IMGONNARUNFORIT"); ++MODULE_VERSION("0.1"); ++MODULE_LICENSE("GPL"); ++ ++static int __init my_module_init(void) { ++ printk(KERN_INFO "DMR driver has been loaded\n"); ++ return 0; ++} ++ ++static void __exit my_module_exit(void) { ++ printk(KERN_INFO "DMR driver has been unloaded\n"); ++} ++ ++module_init(my_module_init); ++module_exit(my_module_exit); +diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c +new file mode 100644 +index 000000000000..2e28726bbd53 +--- /dev/null ++++ b/drivers/media/i2c/imx708.c +@@ -0,0 +1,2193 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * A V4L2 driver for Sony IMX708 cameras. ++ * Copyright (C) 2022, Raspberry Pi Ltd ++ * ++ * Based on Sony imx708 camera driver ++ * Copyright (C) 2020 Raspberry Pi Ltd ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x03) ++#define IMX708_NAME "imx708" ++#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" ++ ++static int debug = 0; ++module_param(debug, int, 0644); ++ ++#define IMX708_REG_VALUE_08BIT 1 ++#define IMX708_REG_VALUE_16BIT 2 ++ ++/* Chip ID */ ++#define IMX708_REG_CHIP_ID 0x0016 ++#define IMX708_CHIP_ID 0x0708 ++ ++#define IMX708_REG_MODE_SELECT 0x0100 ++#define IMX708_MODE_STANDBY 0x00 ++#define IMX708_MODE_STREAMING 0x01 ++ ++#define IMX708_REG_ORIENTATION 0x101 ++ ++#define IMX708_INCLK_FREQ 24000000 ++ ++/* Default initial pixel rate, will get updated for each mode. */ ++#define IMX708_INITIAL_PIXEL_RATE 590000000 ++ ++/* V_TIMING internal */ ++#define IMX708_REG_FRAME_LENGTH 0x0340 ++#define IMX708_FRAME_LENGTH_MAX 0xffff ++ ++/* Long exposure multiplier */ ++#define IMX708_LONG_EXP_SHIFT_MAX 7 ++#define IMX708_LONG_EXP_SHIFT_REG 0x3100 ++ ++/* Exposure control */ ++#define IMX708_REG_EXPOSURE 0x0202 ++#define IMX708_EXPOSURE_OFFSET 48 ++#define IMX708_EXPOSURE_DEFAULT 0x640 ++#define IMX708_EXPOSURE_STEP 1 ++#define IMX708_EXPOSURE_MIN 1 ++#define IMX708_EXPOSURE_MAX (IMX708_FRAME_LENGTH_MAX - \ ++ IMX708_EXPOSURE_OFFSET) ++ ++/* Analog gain control */ ++#define IMX708_REG_ANALOG_GAIN 0x0204 ++#define IMX708_ANA_GAIN_MIN 112 ++#define IMX708_ANA_GAIN_MAX 960 ++#define IMX708_ANA_GAIN_STEP 1 ++#define IMX708_ANA_GAIN_DEFAULT IMX708_ANA_GAIN_MIN ++ ++/* Digital gain control */ ++#define IMX708_REG_DIGITAL_GAIN 0x020e ++#define IMX708_DGTL_GAIN_MIN 0x0100 ++#define IMX708_DGTL_GAIN_MAX 0xffff ++#define IMX708_DGTL_GAIN_DEFAULT 0x0100 ++#define IMX708_DGTL_GAIN_STEP 1 ++ ++/* Colour balance controls */ ++#define IMX708_REG_COLOUR_BALANCE_RED 0x0b90 ++#define IMX708_REG_COLOUR_BALANCE_BLUE 0x0b92 ++#define IMX708_COLOUR_BALANCE_MIN 0x01 ++#define IMX708_COLOUR_BALANCE_MAX 0xffff ++#define IMX708_COLOUR_BALANCE_STEP 0x01 ++#define IMX708_COLOUR_BALANCE_DEFAULT 0x100 ++ ++/* Test Pattern Control */ ++#define IMX708_REG_TEST_PATTERN 0x0600 ++#define IMX708_TEST_PATTERN_DISABLE 0 ++#define IMX708_TEST_PATTERN_SOLID_COLOR 1 ++#define IMX708_TEST_PATTERN_COLOR_BARS 2 ++#define IMX708_TEST_PATTERN_GREY_COLOR 3 ++#define IMX708_TEST_PATTERN_PN9 4 ++ ++/* Test pattern colour components */ ++#define IMX708_REG_TEST_PATTERN_R 0x0602 ++#define IMX708_REG_TEST_PATTERN_GR 0x0604 ++#define IMX708_REG_TEST_PATTERN_B 0x0606 ++#define IMX708_REG_TEST_PATTERN_GB 0x0608 ++#define IMX708_TEST_PATTERN_COLOUR_MIN 0 ++#define IMX708_TEST_PATTERN_COLOUR_MAX 0x0fff ++#define IMX708_TEST_PATTERN_COLOUR_STEP 1 ++ ++#define IMX708_REG_BASE_SPC_GAINS_L 0x7b10 ++#define IMX708_REG_BASE_SPC_GAINS_R 0x7c00 ++ ++/* HDR exposure ratio (long:med == med:short) */ ++#define IMX708_HDR_EXPOSURE_RATIO 4 ++#define IMX708_REG_MID_EXPOSURE 0x3116 ++#define IMX708_REG_SHT_EXPOSURE 0x0224 ++#define IMX708_REG_MID_ANALOG_GAIN 0x3118 ++#define IMX708_REG_SHT_ANALOG_GAIN 0x0216 ++ ++/* ++ * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12). ++ * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels) ++ * of embedded data, one line of PDAF data, and two lines of AE-HIST data ++ * (AE histograms are valid for HDR mode and empty in non-HDR modes). ++ */ ++#define IMX708_EMBEDDED_LINE_WIDTH (5 * 5760) ++#define IMX708_NUM_EMBEDDED_LINES 1 ++ ++/* IMX708 native and active pixel array size. */ ++#define IMX708_NATIVE_WIDTH 4640U ++#define IMX708_NATIVE_HEIGHT 2658U ++#define IMX708_PIXEL_ARRAY_LEFT 16U ++#define IMX708_PIXEL_ARRAY_TOP 24U ++#define IMX708_PIXEL_ARRAY_WIDTH 4608U ++#define IMX708_PIXEL_ARRAY_HEIGHT 2592U ++ ++struct imx708_reg { ++ u16 address; ++ u8 val; ++}; ++ ++struct imx708_reg_list { ++ unsigned int num_of_regs; ++ const struct imx708_reg *regs; ++}; ++ ++/* Mode : resolution and related config&values */ ++struct imx708_mode { ++ u32 bus_fmt; ++ ++ /* Frame width */ ++ unsigned int width; ++ ++ /* Frame height */ ++ unsigned int height; ++ ++ struct v4l2_fract max_fps; ++ ++ /* H-timing in pixels */ ++ unsigned int line_length_pix; ++ ++ /* Analog crop rectangle. */ ++ struct v4l2_rect crop; ++ ++ /* Highest possible framerate. */ ++ unsigned int vblank_min; ++ ++ /* Default framerate. */ ++ unsigned int vblank_default; ++ ++ /* Default register values */ ++ struct imx708_reg_list reg_list; ++ ++ /* Not all modes have the same pixel rate. */ ++ u64 pixel_rate; ++ ++ /* Not all modes have the same minimum exposure. */ ++ u32 exposure_lines_min; ++ ++ /* Not all modes have the same exposure lines step. */ ++ u32 exposure_lines_step; ++ ++ u32 hdr_mode; ++}; ++ ++/* Default PDAF pixel correction gains */ ++static const u8 pdaf_gains[2][9] = { ++ { 0x4c, 0x4c, 0x4c, 0x46, 0x3e, 0x38, 0x35, 0x35, 0x35 }, ++ { 0x35, 0x35, 0x35, 0x38, 0x3e, 0x46, 0x4c, 0x4c, 0x4c } ++}; ++ ++/* Link frequency setup */ ++enum { ++ IMX708_LINK_FREQ_450MHZ, ++ IMX708_LINK_FREQ_447MHZ, ++ IMX708_LINK_FREQ_453MHZ, ++}; ++ ++static const s64 link_freqs[] = { ++ [IMX708_LINK_FREQ_450MHZ] = 450000000, ++ [IMX708_LINK_FREQ_447MHZ] = 447000000, ++ [IMX708_LINK_FREQ_453MHZ] = 453000000, ++}; ++ ++/* 450MHz is the nominal "default" link frequency */ ++static const struct imx708_reg link_450Mhz_regs[] = { ++ {0x030E, 0x01}, ++ {0x030F, 0x2c}, ++}; ++ ++static const struct imx708_reg link_447Mhz_regs[] = { ++ {0x030E, 0x01}, ++ {0x030F, 0x2a}, ++}; ++ ++static const struct imx708_reg link_453Mhz_regs[] = { ++ {0x030E, 0x01}, ++ {0x030F, 0x2e}, ++}; ++ ++static const struct imx708_reg_list link_freq_regs[] = { ++ [IMX708_LINK_FREQ_450MHZ] = { ++ .regs = link_450Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_450Mhz_regs) ++ }, ++ [IMX708_LINK_FREQ_447MHZ] = { ++ .regs = link_447Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_447Mhz_regs) ++ }, ++ [IMX708_LINK_FREQ_453MHZ] = { ++ .regs = link_453Mhz_regs, ++ .num_of_regs = ARRAY_SIZE(link_453Mhz_regs) ++ }, ++}; ++ ++static const struct imx708_reg mode_common_regs[] = { ++ {0x0100, 0x00}, ++ {0x0136, 0x18}, ++ {0x0137, 0x00}, ++ {0x33F0, 0x02}, ++ {0x33F1, 0x05}, ++ {0x3062, 0x00}, ++ {0x3063, 0x12}, ++ {0x3068, 0x00}, ++ {0x3069, 0x12}, ++ {0x306A, 0x00}, ++ {0x306B, 0x30}, ++ {0x3076, 0x00}, ++ {0x3077, 0x30}, ++ {0x3078, 0x00}, ++ {0x3079, 0x30}, ++ {0x5E54, 0x0C}, ++ {0x6E44, 0x00}, ++ {0xB0B6, 0x01}, ++ {0xE829, 0x00}, ++ {0xF001, 0x08}, ++ {0xF003, 0x08}, ++ {0xF00D, 0x10}, ++ {0xF00F, 0x10}, ++ {0xF031, 0x08}, ++ {0xF033, 0x08}, ++ {0xF03D, 0x10}, ++ {0xF03F, 0x10}, ++ {0x0112, 0x0A}, ++ {0x0113, 0x0A}, ++ {0x0114, 0x01}, ++ {0x0B8E, 0x01}, ++ {0x0B8F, 0x00}, ++ {0x0B94, 0x01}, ++ {0x0B95, 0x00}, ++ {0x3400, 0x01}, ++ {0x3478, 0x01}, ++ {0x3479, 0x1c}, ++ {0x3091, 0x01}, ++ {0x3092, 0x00}, ++ {0x3419, 0x00}, ++ {0xBCF1, 0x02}, ++ {0x3094, 0x01}, ++ {0x3095, 0x01}, ++ {0x3362, 0x00}, ++ {0x3363, 0x00}, ++ {0x3364, 0x00}, ++ {0x3365, 0x00}, ++ {0x0138, 0x01}, ++}; ++ ++/* 10-bit. */ ++static const struct imx708_reg mode_4608x2592_regs[] = { ++ {0x0342, 0x3D}, ++ {0x0343, 0x20}, ++ {0x0340, 0x0A}, ++ {0x0341, 0x59}, ++ {0x0344, 0x00}, ++ {0x0345, 0x00}, ++ {0x0346, 0x00}, ++ {0x0347, 0x00}, ++ {0x0348, 0x11}, ++ {0x0349, 0xFF}, ++ {0x034A, 0X0A}, ++ {0x034B, 0x1F}, ++ {0x0220, 0x62}, ++ {0x0222, 0x01}, ++ {0x0900, 0x00}, ++ {0x0901, 0x11}, ++ {0x0902, 0x0A}, ++ {0x3200, 0x01}, ++ {0x3201, 0x01}, ++ {0x32D5, 0x01}, ++ {0x32D6, 0x00}, ++ {0x32DB, 0x01}, ++ {0x32DF, 0x00}, ++ {0x350C, 0x00}, ++ {0x350D, 0x00}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040A, 0x00}, ++ {0x040B, 0x00}, ++ {0x040C, 0x12}, ++ {0x040D, 0x00}, ++ {0x040E, 0x0A}, ++ {0x040F, 0x20}, ++ {0x034C, 0x12}, ++ {0x034D, 0x00}, ++ {0x034E, 0x0A}, ++ {0x034F, 0x20}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x02}, ++ {0x0306, 0x00}, ++ {0x0307, 0x7C}, ++ {0x030B, 0x02}, ++ {0x030D, 0x04}, ++ {0x0310, 0x01}, ++ {0x3CA0, 0x00}, ++ {0x3CA1, 0x64}, ++ {0x3CA4, 0x00}, ++ {0x3CA5, 0x00}, ++ {0x3CA6, 0x00}, ++ {0x3CA7, 0x00}, ++ {0x3CAA, 0x00}, ++ {0x3CAB, 0x00}, ++ {0x3CB8, 0x00}, ++ {0x3CB9, 0x08}, ++ {0x3CBA, 0x00}, ++ {0x3CBB, 0x00}, ++ {0x3CBC, 0x00}, ++ {0x3CBD, 0x3C}, ++ {0x3CBE, 0x00}, ++ {0x3CBF, 0x00}, ++ {0x0202, 0x0A}, ++ {0x0203, 0x29}, ++ {0x0224, 0x01}, ++ {0x0225, 0xF4}, ++ {0x3116, 0x01}, ++ {0x3117, 0xF4}, ++ {0x0204, 0x00}, ++ {0x0205, 0x00}, ++ {0x0216, 0x00}, ++ {0x0217, 0x00}, ++ {0x0218, 0x01}, ++ {0x0219, 0x00}, ++ {0x020E, 0x01}, ++ {0x020F, 0x00}, ++ {0x3118, 0x00}, ++ {0x3119, 0x00}, ++ {0x311A, 0x01}, ++ {0x311B, 0x00}, ++ {0x341a, 0x00}, ++ {0x341b, 0x00}, ++ {0x341c, 0x00}, ++ {0x341d, 0x00}, ++ {0x341e, 0x01}, ++ {0x341f, 0x20}, ++ {0x3420, 0x00}, ++ {0x3421, 0xd8}, ++ {0xC428, 0x00}, ++ {0xC429, 0x04}, ++ {0x3366, 0x00}, ++ {0x3367, 0x00}, ++ {0x3368, 0x00}, ++ {0x3369, 0x00}, ++}; ++ ++static const struct imx708_reg mode_2x2binned_regs[] = { ++ {0x0342, 0x1E}, ++ {0x0343, 0x90}, ++ {0x0340, 0x05}, ++ {0x0341, 0x38}, ++ {0x0344, 0x00}, ++ {0x0345, 0x00}, ++ {0x0346, 0x00}, ++ {0x0347, 0x00}, ++ {0x0348, 0x11}, ++ {0x0349, 0xFF}, ++ {0x034A, 0X0A}, ++ {0x034B, 0x1F}, ++ {0x0220, 0x62}, ++ {0x0222, 0x01}, ++ {0x0900, 0x01}, ++ {0x0901, 0x22}, ++ {0x0902, 0x08}, ++ {0x3200, 0x41}, ++ {0x3201, 0x41}, ++ {0x32D5, 0x00}, ++ {0x32D6, 0x00}, ++ {0x32DB, 0x01}, ++ {0x32DF, 0x00}, ++ {0x350C, 0x00}, ++ {0x350D, 0x00}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040A, 0x00}, ++ {0x040B, 0x00}, ++ {0x040C, 0x09}, ++ {0x040D, 0x00}, ++ {0x040E, 0x05}, ++ {0x040F, 0x10}, ++ {0x034C, 0x09}, ++ {0x034D, 0x00}, ++ {0x034E, 0x05}, ++ {0x034F, 0x10}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x02}, ++ {0x0306, 0x00}, ++ {0x0307, 0x7A}, ++ {0x030B, 0x02}, ++ {0x030D, 0x04}, ++ {0x0310, 0x01}, ++ {0x3CA0, 0x00}, ++ {0x3CA1, 0x3C}, ++ {0x3CA4, 0x00}, ++ {0x3CA5, 0x3C}, ++ {0x3CA6, 0x00}, ++ {0x3CA7, 0x00}, ++ {0x3CAA, 0x00}, ++ {0x3CAB, 0x00}, ++ {0x3CB8, 0x00}, ++ {0x3CB9, 0x1C}, ++ {0x3CBA, 0x00}, ++ {0x3CBB, 0x08}, ++ {0x3CBC, 0x00}, ++ {0x3CBD, 0x1E}, ++ {0x3CBE, 0x00}, ++ {0x3CBF, 0x0A}, ++ {0x0202, 0x05}, ++ {0x0203, 0x08}, ++ {0x0224, 0x01}, ++ {0x0225, 0xF4}, ++ {0x3116, 0x01}, ++ {0x3117, 0xF4}, ++ {0x0204, 0x00}, ++ {0x0205, 0x70}, ++ {0x0216, 0x00}, ++ {0x0217, 0x70}, ++ {0x0218, 0x01}, ++ {0x0219, 0x00}, ++ {0x020E, 0x01}, ++ {0x020F, 0x00}, ++ {0x3118, 0x00}, ++ {0x3119, 0x70}, ++ {0x311A, 0x01}, ++ {0x311B, 0x00}, ++ {0x341a, 0x00}, ++ {0x341b, 0x00}, ++ {0x341c, 0x00}, ++ {0x341d, 0x00}, ++ {0x341e, 0x00}, ++ {0x341f, 0x90}, ++ {0x3420, 0x00}, ++ {0x3421, 0x6c}, ++ {0x3366, 0x07}, ++ {0x3367, 0x80}, ++ {0x3368, 0x04}, ++ {0x3369, 0x38}, ++}; ++ ++static const struct imx708_reg mode_2x2binned_720p_regs[] = { ++ {0x0342, 0x14}, ++ {0x0343, 0x60}, ++ {0x0340, 0x04}, ++ {0x0341, 0xB6}, ++ {0x0344, 0x03}, ++ {0x0345, 0x00}, ++ {0x0346, 0x01}, ++ {0x0347, 0xB0}, ++ {0x0348, 0x0E}, ++ {0x0349, 0xFF}, ++ {0x034A, 0x08}, ++ {0x034B, 0x6F}, ++ {0x0220, 0x62}, ++ {0x0222, 0x01}, ++ {0x0900, 0x01}, ++ {0x0901, 0x22}, ++ {0x0902, 0x08}, ++ {0x3200, 0x41}, ++ {0x3201, 0x41}, ++ {0x32D5, 0x00}, ++ {0x32D6, 0x00}, ++ {0x32DB, 0x01}, ++ {0x32DF, 0x01}, ++ {0x350C, 0x00}, ++ {0x350D, 0x00}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040A, 0x00}, ++ {0x040B, 0x00}, ++ {0x040C, 0x06}, ++ {0x040D, 0x00}, ++ {0x040E, 0x03}, ++ {0x040F, 0x60}, ++ {0x034C, 0x06}, ++ {0x034D, 0x00}, ++ {0x034E, 0x03}, ++ {0x034F, 0x60}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x02}, ++ {0x0306, 0x00}, ++ {0x0307, 0x76}, ++ {0x030B, 0x02}, ++ {0x030D, 0x04}, ++ {0x0310, 0x01}, ++ {0x3CA0, 0x00}, ++ {0x3CA1, 0x3C}, ++ {0x3CA4, 0x01}, ++ {0x3CA5, 0x5E}, ++ {0x3CA6, 0x00}, ++ {0x3CA7, 0x00}, ++ {0x3CAA, 0x00}, ++ {0x3CAB, 0x00}, ++ {0x3CB8, 0x00}, ++ {0x3CB9, 0x0C}, ++ {0x3CBA, 0x00}, ++ {0x3CBB, 0x04}, ++ {0x3CBC, 0x00}, ++ {0x3CBD, 0x1E}, ++ {0x3CBE, 0x00}, ++ {0x3CBF, 0x05}, ++ {0x0202, 0x04}, ++ {0x0203, 0x86}, ++ {0x0224, 0x01}, ++ {0x0225, 0xF4}, ++ {0x3116, 0x01}, ++ {0x3117, 0xF4}, ++ {0x0204, 0x00}, ++ {0x0205, 0x70}, ++ {0x0216, 0x00}, ++ {0x0217, 0x70}, ++ {0x0218, 0x01}, ++ {0x0219, 0x00}, ++ {0x020E, 0x01}, ++ {0x020F, 0x00}, ++ {0x3118, 0x00}, ++ {0x3119, 0x70}, ++ {0x311A, 0x01}, ++ {0x311B, 0x00}, ++ {0x341a, 0x00}, ++ {0x341b, 0x00}, ++ {0x341c, 0x00}, ++ {0x341d, 0x00}, ++ {0x341e, 0x00}, ++ {0x341f, 0x60}, ++ {0x3420, 0x00}, ++ {0x3421, 0x48}, ++ {0x3366, 0x00}, ++ {0x3367, 0x00}, ++ {0x3368, 0x00}, ++ {0x3369, 0x00}, ++}; ++ ++static const struct imx708_reg mode_hdr_regs[] = { ++ {0x0342, 0x14}, ++ {0x0343, 0x60}, ++ {0x0340, 0x0A}, ++ {0x0341, 0x5B}, ++ {0x0344, 0x00}, ++ {0x0345, 0x00}, ++ {0x0346, 0x00}, ++ {0x0347, 0x00}, ++ {0x0348, 0x11}, ++ {0x0349, 0xFF}, ++ {0x034A, 0X0A}, ++ {0x034B, 0x1F}, ++ {0x0220, 0x01}, ++ {0x0222, IMX708_HDR_EXPOSURE_RATIO}, ++ {0x0900, 0x00}, ++ {0x0901, 0x11}, ++ {0x0902, 0x0A}, ++ {0x3200, 0x01}, ++ {0x3201, 0x01}, ++ {0x32D5, 0x00}, ++ {0x32D6, 0x00}, ++ {0x32DB, 0x01}, ++ {0x32DF, 0x00}, ++ {0x350C, 0x00}, ++ {0x350D, 0x00}, ++ {0x0408, 0x00}, ++ {0x0409, 0x00}, ++ {0x040A, 0x00}, ++ {0x040B, 0x00}, ++ {0x040C, 0x09}, ++ {0x040D, 0x00}, ++ {0x040E, 0x05}, ++ {0x040F, 0x10}, ++ {0x034C, 0x09}, ++ {0x034D, 0x00}, ++ {0x034E, 0x05}, ++ {0x034F, 0x10}, ++ {0x0301, 0x05}, ++ {0x0303, 0x02}, ++ {0x0305, 0x02}, ++ {0x0306, 0x00}, ++ {0x0307, 0xA2}, ++ {0x030B, 0x02}, ++ {0x030D, 0x04}, ++ {0x0310, 0x01}, ++ {0x3CA0, 0x00}, ++ {0x3CA1, 0x00}, ++ {0x3CA4, 0x00}, ++ {0x3CA5, 0x00}, ++ {0x3CA6, 0x00}, ++ {0x3CA7, 0x28}, ++ {0x3CAA, 0x00}, ++ {0x3CAB, 0x00}, ++ {0x3CB8, 0x00}, ++ {0x3CB9, 0x30}, ++ {0x3CBA, 0x00}, ++ {0x3CBB, 0x00}, ++ {0x3CBC, 0x00}, ++ {0x3CBD, 0x32}, ++ {0x3CBE, 0x00}, ++ {0x3CBF, 0x00}, ++ {0x0202, 0x0A}, ++ {0x0203, 0x2B}, ++ {0x0224, 0x0A}, ++ {0x0225, 0x2B}, ++ {0x3116, 0x0A}, ++ {0x3117, 0x2B}, ++ {0x0204, 0x00}, ++ {0x0205, 0x00}, ++ {0x0216, 0x00}, ++ {0x0217, 0x00}, ++ {0x0218, 0x01}, ++ {0x0219, 0x00}, ++ {0x020E, 0x01}, ++ {0x020F, 0x00}, ++ {0x3118, 0x00}, ++ {0x3119, 0x00}, ++ {0x311A, 0x01}, ++ {0x311B, 0x00}, ++ {0x341a, 0x00}, ++ {0x341b, 0x00}, ++ {0x341c, 0x00}, ++ {0x341d, 0x00}, ++ {0x341e, 0x00}, ++ {0x341f, 0x90}, ++ {0x3420, 0x00}, ++ {0x3421, 0x6c}, ++ {0x3360, 0x01}, ++ {0x3361, 0x01}, ++ {0x3366, 0x07}, ++ {0x3367, 0x80}, ++ {0x3368, 0x04}, ++ {0x3369, 0x38}, ++}; ++ ++/* Mode configs. Keep separate lists for when HDR is enabled or not. */ ++static const struct imx708_mode supported_modes[] = { ++ { ++ .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, ++ /* Full resolution. */ ++ .width = 4608, ++ .height = 2592, ++ .max_fps = { ++ .numerator = 10000, ++ .denominator = 140000, ++ }, ++ .line_length_pix = 0x3d20, ++ .crop = { ++ .left = IMX708_PIXEL_ARRAY_LEFT, ++ .top = IMX708_PIXEL_ARRAY_TOP, ++ .width = 4608, ++ .height = 2592, ++ }, ++ .vblank_min = 58, ++ .vblank_default = 58, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_4608x2592_regs), ++ .regs = mode_4608x2592_regs, ++ }, ++ .pixel_rate = 595200000, ++ .exposure_lines_min = 8, ++ .exposure_lines_step = 1, ++ .hdr_mode = NO_HDR, ++ }, ++ { ++ .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, ++ /* regular 2x2 binned. */ ++ .width = 1920, ++ .height = 1080, ++ .max_fps = { ++ .numerator = 10000, ++ .denominator = 660000, ++ }, ++ .line_length_pix = 0x1e90, ++ .crop = { ++ .left = IMX708_PIXEL_ARRAY_LEFT, ++ .top = IMX708_PIXEL_ARRAY_TOP, ++ .width = 4608, ++ .height = 2592, ++ }, ++ .vblank_min = 40, ++ .vblank_default = 1198, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_2x2binned_regs), ++ .regs = mode_2x2binned_regs, ++ }, ++ .pixel_rate = 585600000, ++ .exposure_lines_min = 4, ++ .exposure_lines_step = 2, ++ .hdr_mode = NO_HDR, ++ }, ++ { ++ .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, ++ /* There's only one HDR mode, which is 2x2 downscaled */ ++ .width = 1920, ++ .height = 1080, ++ .max_fps = { ++ .numerator = 10000, ++ .denominator = 310000, ++ }, ++ .line_length_pix = 0x1460, ++ .crop = { ++ .left = IMX708_PIXEL_ARRAY_LEFT, ++ .top = IMX708_PIXEL_ARRAY_TOP, ++ .width = 4608, ++ .height = 2592, ++ }, ++ .vblank_min = 3673, ++ .vblank_default = 3673, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_hdr_regs), ++ .regs = mode_hdr_regs, ++ }, ++ .pixel_rate = 777600000, ++ .exposure_lines_min = 8 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO, ++ .exposure_lines_step = 2 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO, ++ .hdr_mode = HDR_X3, ++ }, ++ { ++ .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, ++ /* 2x2 binned and cropped for 720p. */ ++ .width = 1536, ++ .height = 864, ++ .max_fps = { ++ .numerator = 10000, ++ .denominator = 1200000, ++ }, ++ .line_length_pix = 0x1460, ++ .crop = { ++ .left = IMX708_PIXEL_ARRAY_LEFT + 768, ++ .top = IMX708_PIXEL_ARRAY_TOP + 432, ++ .width = 3072, ++ .height = 1728, ++ }, ++ .vblank_min = 40, ++ .vblank_default = 2755, ++ .reg_list = { ++ .num_of_regs = ARRAY_SIZE(mode_2x2binned_720p_regs), ++ .regs = mode_2x2binned_720p_regs, ++ }, ++ .pixel_rate = 566400000, ++ .exposure_lines_min = 4, ++ .exposure_lines_step = 2, ++ .hdr_mode = NO_HDR, ++ }, ++}; ++ ++ ++static const char * const imx708_test_pattern_menu[] = { ++ "Disabled", ++ "Color Bars", ++ "Solid Color", ++ "Grey Color Bars", ++ "PN9" ++}; ++ ++static const int imx708_test_pattern_val[] = { ++ IMX708_TEST_PATTERN_DISABLE, ++ IMX708_TEST_PATTERN_COLOR_BARS, ++ IMX708_TEST_PATTERN_SOLID_COLOR, ++ IMX708_TEST_PATTERN_GREY_COLOR, ++ IMX708_TEST_PATTERN_PN9, ++}; ++ ++/* regulator supplies */ ++static const char * const imx708_supply_name[] = { ++ /* Supplies can be enabled in any order */ ++ "vana1", /* Analog1 (2.8V) supply */ ++ "vana2", /* Analog2 (1.8V) supply */ ++ "vdig", /* Digital Core (1.1V) supply */ ++ "vddl", /* IF (1.8V) supply */ ++}; ++ ++/* ++ * Initialisation delay between XCLR low->high and the moment when the sensor ++ * can start capture (i.e. can leave software standby), given by T7 in the ++ * datasheet is 8ms. This does include I2C setup time as well. ++ * ++ * Note, that delay between XCLR low->high and reading the CCI ID register (T6 ++ * in the datasheet) is much smaller - 600us. ++ */ ++#define IMX708_XCLR_MIN_DELAY_US 8000 ++#define IMX708_XCLR_DELAY_RANGE_US 1000 ++ ++struct imx708 { ++ struct i2c_client *client; ++ struct clk *inclk; ++ struct gpio_desc *reset_gpio; ++ ++ struct regulator_bulk_data supplies[ARRAY_SIZE(imx708_supply_name)]; ++ ++ struct v4l2_mbus_framefmt fmt; ++ ++ u32 inclk_freq; ++ ++ struct v4l2_subdev subdev; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler ctrl_handler; ++ /* V4L2 Controls */ ++ struct v4l2_ctrl *pixel_rate; ++ struct v4l2_ctrl *exposure; ++ struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *link_freq; ++ struct { ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *vflip; ++ }; ++ ++ /* ++ * Mutex for serialized access: ++ * Protect sensor module set pad format and start/stop streaming safely. ++ */ ++ struct mutex mutex; ++ ++ /* Streaming on/off */ ++ bool streaming; ++ bool power_on; ++ ++ /* Current mode */ ++ const struct imx708_mode *cur_mode; ++ /*module*/ ++ u32 module_index; ++ u32 cfg_num; ++ const char *module_facing; ++ const char *module_name; ++ const char *len_name; ++ ++ /* Rewrite common registers on stream on? */ ++ bool common_regs_written; ++ ++ /* Current long exposure factor in use. Set through V4L2_CID_VBLANK */ ++ unsigned int long_exp_shift; ++ ++ unsigned int link_freq_idx; ++}; ++ ++static inline struct imx708 *to_imx708(struct v4l2_subdev *_sd) ++{ ++ return container_of(_sd, struct imx708, subdev); ++} ++ ++/* Read registers up to 2 at a time */ ++static int imx708_read_reg(struct imx708 *imx708, u16 reg, u32 len, u32 *val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ struct i2c_msg msgs[2]; ++ u8 addr_buf[2] = { reg >> 8, reg & 0xff }; ++ u8 data_buf[4] = { 0, }; ++ int ret; ++ ++ if (len > 4) ++ return -EINVAL; ++ ++ /* Write register address */ ++ msgs[0].addr = client->addr; ++ msgs[0].flags = 0; ++ msgs[0].len = ARRAY_SIZE(addr_buf); ++ msgs[0].buf = addr_buf; ++ ++ /* Read data from register */ ++ msgs[1].addr = client->addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = len; ++ msgs[1].buf = &data_buf[4 - len]; ++ ++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (ret != ARRAY_SIZE(msgs)) ++ return -EIO; ++ ++ *val = get_unaligned_be32(data_buf); ++ ++ return 0; ++} ++ ++/* Write registers up to 2 at a time */ ++static int imx708_write_reg(struct imx708 *imx708, u16 reg, u32 len, u32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ u8 buf[6]; ++ ++ if (len > 4) ++ return -EINVAL; ++ ++ put_unaligned_be16(reg, buf); ++ put_unaligned_be32(val << (8 * (4 - len)), buf + 2); ++ if (i2c_master_send(client, buf, len + 2) != len + 2) ++ return -EIO; ++ ++ return 0; ++} ++ ++/* Write a list of registers */ ++static int imx708_write_regs(struct imx708 *imx708, ++ const struct imx708_reg *regs, u32 len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ unsigned int i; ++ ++ for (i = 0; i < len; i++) { ++ int ret; ++ ++ ret = imx708_write_reg(imx708, regs[i].address, 1, regs[i].val); ++ if (ret) { ++ dev_err_ratelimited(&client->dev, ++ "Failed to write reg 0x%4.4x. error = %d\n", ++ regs[i].address, ret); ++ ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API ++static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ struct v4l2_mbus_framefmt *try_fmt_img = ++ v4l2_subdev_get_try_format(sd, fh->pad, 0); ++ ++ mutex_lock(&imx708->mutex); ++ ++ try_fmt_img->width = supported_modes[0].width; ++ try_fmt_img->height = supported_modes[0].height; ++ try_fmt_img->code = supported_modes[0].bus_fmt; ++ try_fmt_img->field = V4L2_FIELD_NONE; ++ ++ mutex_unlock(&imx708->mutex); ++ ++ return 0; ++} ++#endif ++ ++static int imx708_set_exposure(struct imx708 *imx708, unsigned int val) ++{ ++ val = max(val, imx708->cur_mode->exposure_lines_min); ++ val -= val % imx708->cur_mode->exposure_lines_step; ++ ++ /* ++ * In HDR mode this will set the longest exposure. The sensor ++ * will automatically divide the medium and short ones by 4,16. ++ */ ++ return imx708_write_reg(imx708, IMX708_REG_EXPOSURE, ++ IMX708_REG_VALUE_16BIT, ++ val >> imx708->long_exp_shift); ++} ++ ++static void imx708_adjust_exposure_range(struct imx708 *imx708, ++ struct v4l2_ctrl *ctrl) ++{ ++ int exposure_max, exposure_def; ++ ++ /* Honour the VBLANK limits when setting exposure. */ ++ exposure_max = imx708->cur_mode->height + imx708->vblank->val - ++ IMX708_EXPOSURE_OFFSET; ++ exposure_def = min(exposure_max, imx708->exposure->val); ++ __v4l2_ctrl_modify_range(imx708->exposure, imx708->exposure->minimum, ++ exposure_max, imx708->exposure->step, ++ exposure_def); ++} ++ ++static int imx708_set_analogue_gain(struct imx708 *imx708, unsigned int val) ++{ ++ int ret; ++ ++ /* ++ * In HDR mode this will set the gain for the longest exposure, ++ * and by default the sensor uses the same gain for all of them. ++ */ ++ ret = imx708_write_reg(imx708, IMX708_REG_ANALOG_GAIN, ++ IMX708_REG_VALUE_16BIT, val); ++ ++ return ret; ++} ++ ++static int imx708_set_frame_length(struct imx708 *imx708, unsigned int val) ++{ ++ int ret; ++ ++ imx708->long_exp_shift = 0; ++ ++ while (val > IMX708_FRAME_LENGTH_MAX) { ++ imx708->long_exp_shift++; ++ val >>= 1; ++ } ++ ++ ret = imx708_write_reg(imx708, IMX708_REG_FRAME_LENGTH, ++ IMX708_REG_VALUE_16BIT, val); ++ if (ret) ++ return ret; ++ ++ return imx708_write_reg(imx708, IMX708_LONG_EXP_SHIFT_REG, ++ IMX708_REG_VALUE_08BIT, imx708->long_exp_shift); ++} ++ ++static void imx708_set_framing_limits(struct imx708 *imx708) ++{ ++ const struct imx708_mode *mode = imx708->cur_mode; ++ unsigned int hblank; ++ ++ __v4l2_ctrl_modify_range(imx708->pixel_rate, ++ mode->pixel_rate, mode->pixel_rate, ++ 1, mode->pixel_rate); ++ ++ /* Update limits and set FPS to default */ ++ __v4l2_ctrl_modify_range(imx708->vblank, mode->vblank_min, ++ ((1 << IMX708_LONG_EXP_SHIFT_MAX) * ++ IMX708_FRAME_LENGTH_MAX) - mode->height, ++ 1, mode->vblank_default); ++ ++ /* ++ * Currently PPL is fixed to the mode specified value, so hblank ++ * depends on mode->width only, and is not changeable in any ++ * way other than changing the mode. ++ */ ++ hblank = mode->line_length_pix - mode->width; ++ __v4l2_ctrl_modify_range(imx708->hblank, hblank, hblank, 1, hblank); ++} ++ ++static int imx708_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct imx708 *imx708 = ++ container_of(ctrl->handler, struct imx708, ctrl_handler); ++ struct i2c_client *client = imx708->client; ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VBLANK: ++ /* ++ * The VBLANK control may change the limits of usable exposure, ++ * so check and adjust if necessary. ++ */ ++ imx708_adjust_exposure_range(imx708, ctrl); ++ break; ++ } ++ ++ /* ++ * Applying V4L2 control value only happens ++ * when power is up for streaming ++ */ ++ if (pm_runtime_get_if_in_use(&client->dev) == 0) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ANALOGUE_GAIN: ++ imx708_set_analogue_gain(imx708, ctrl->val); ++ break; ++ case V4L2_CID_EXPOSURE: ++ ret = imx708_set_exposure(imx708, ctrl->val); ++ break; ++ case V4L2_CID_DIGITAL_GAIN: ++ ret = imx708_write_reg(imx708, IMX708_REG_DIGITAL_GAIN, ++ IMX708_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN: ++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN, ++ IMX708_REG_VALUE_16BIT, ++ imx708_test_pattern_val[ctrl->val]); ++ break; ++ case V4L2_CID_TEST_PATTERN_RED: ++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_R, ++ IMX708_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN_GREENR: ++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GR, ++ IMX708_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN_BLUE: ++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_B, ++ IMX708_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_TEST_PATTERN_GREENB: ++ ret = imx708_write_reg(imx708, IMX708_REG_TEST_PATTERN_GB, ++ IMX708_REG_VALUE_16BIT, ctrl->val); ++ break; ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: ++ ret = imx708_write_reg(imx708, IMX708_REG_ORIENTATION, 1, ++ imx708->hflip->val | ++ imx708->vflip->val << 1); ++ break; ++ case V4L2_CID_VBLANK: ++ ret = imx708_set_frame_length(imx708, ++ imx708->cur_mode->height + ctrl->val); ++ break; ++ default: ++ dev_info(&client->dev, ++ "ctrl(id:0x%x,val:0x%x) is not handled\n", ++ ctrl->id, ctrl->val); ++ ret = -EINVAL; ++ break; ++ } ++ ++ pm_runtime_put(&client->dev); ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops imx708_ctrl_ops = { ++ .s_ctrl = imx708_set_ctrl, ++}; ++ ++static int imx708_g_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ const struct imx708_mode *mode = imx708->cur_mode; ++ ++ mutex_lock(&imx708->mutex); ++ fi->interval = mode->max_fps; ++ mutex_unlock(&imx708->mutex); ++ ++ return 0; ++} ++ ++static int imx708_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ if (code->index >= imx708->cfg_num) ++ return -EINVAL; ++ ++ code->code = supported_modes[code->index].bus_fmt; ++ ++ return 0; ++} ++ ++static int imx708_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ ++ ++ if (fse->index >= imx708->cfg_num) ++ return -EINVAL; ++ ++ if (fse->code != supported_modes[fse->index].bus_fmt) ++ return -EINVAL; ++ ++ fse->min_width = supported_modes[fse->index].width; ++ fse->max_width = fse->min_width;; ++ fse->min_height = supported_modes[fse->index].height; ++ fse->max_height = fse->min_height;; ++ ++ return 0; ++} ++ ++static int imx708_get_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ const struct imx708_mode *mode = imx708->cur_mode; ++ ++ mutex_lock(&imx708->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(&imx477->mutex); ++ return -ENOTTY; ++#endif ++ } else { ++ fmt->format.width = mode->width; ++ fmt->format.height = mode->height; ++ fmt->format.code = mode->bus_fmt; ++ fmt->format.field = V4L2_FIELD_NONE; ++ } ++ ++ mutex_unlock(&imx708->mutex); ++ return 0; ++} ++ ++static int imx708_get_reso_dist(const struct imx708_mode *mode, ++ struct v4l2_mbus_framefmt *framefmt) ++{ ++ return abs(mode->width - framefmt->width) + ++ abs(mode->height - framefmt->height); ++} ++ ++static const struct imx708_mode * ++imx708_find_best_fit(struct imx708 *imx708, 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; ++ unsigned int i; ++ ++ for (i = 0; i < imx708->cfg_num; i++) { ++ dist = imx708_get_reso_dist(&supported_modes[i], framefmt); ++ if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && ++ supported_modes[i].bus_fmt == framefmt->code) { ++ cur_best_fit_dist = dist; ++ cur_best_fit = i; ++ } ++ } ++ dev_info(&imx708->client->dev, "%s: cur_best_fit(%d)", ++ __func__, cur_best_fit); ++ ++ return &supported_modes[cur_best_fit]; ++} ++ ++static int imx708_set_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_format *fmt) ++{ ++ const struct imx708_mode *mode; ++ struct imx708 *imx708 = to_imx708(sd); ++ ++ mutex_lock(&imx708->mutex); ++ mode = imx708_find_best_fit(imx708,fmt); ++ fmt->format.code = mode->bus_fmt; ++ 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(&imx708->mutex); ++ return -ENOTTY; ++#endif ++ } else { ++ imx708->cur_mode = mode; ++ imx708_set_framing_limits(imx708); ++ } ++ ++ mutex_unlock(&imx708->mutex); ++ ++ return 0; ++} ++ ++/* Start streaming */ ++static int imx708_start_streaming(struct imx708 *imx708) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ const struct imx708_reg_list *reg_list, *freq_regs; ++ int i, ret; ++ u32 val; ++ ++ if (!imx708->common_regs_written) { ++ ret = imx708_write_regs(imx708, mode_common_regs, ++ ARRAY_SIZE(mode_common_regs)); ++ if (ret) { ++ dev_err(&client->dev, "%s failed to set common settings\n", ++ __func__); ++ return ret; ++ } ++ ++ ret = imx708_read_reg(imx708, IMX708_REG_BASE_SPC_GAINS_L, ++ IMX708_REG_VALUE_08BIT, &val); ++ if (ret == 0 && val == 0x40) { ++ for (i = 0; i < 54 && ret == 0; i++) { ++ ret = imx708_write_reg(imx708, ++ IMX708_REG_BASE_SPC_GAINS_L + i, ++ IMX708_REG_VALUE_08BIT, ++ pdaf_gains[0][i % 9]); ++ } ++ for (i = 0; i < 54 && ret == 0; i++) { ++ ret = imx708_write_reg(imx708, ++ IMX708_REG_BASE_SPC_GAINS_R + i, ++ IMX708_REG_VALUE_08BIT, ++ pdaf_gains[1][i % 9]); ++ } ++ } ++ if (ret) { ++ dev_err(&client->dev, "%s failed to set PDAF gains\n", ++ __func__); ++ return ret; ++ } ++ ++ imx708->common_regs_written = true; ++ } ++ ++ /* Apply default values of current mode */ ++ reg_list = &imx708->cur_mode->reg_list; ++ ret = imx708_write_regs(imx708, reg_list->regs, reg_list->num_of_regs); ++ if (ret) { ++ dev_err(&client->dev, "%s failed to set mode\n", __func__); ++ return ret; ++ } ++ ++ /* Update the link frequency registers */ ++ freq_regs = &link_freq_regs[imx708->link_freq_idx]; ++ ret = imx708_write_regs(imx708, freq_regs->regs, ++ freq_regs->num_of_regs); ++ if (ret) { ++ dev_err(&client->dev, "%s failed to set link frequency registers\n", ++ __func__); ++ return ret; ++ } ++ ++ /* Apply customized values from user */ ++ ret = __v4l2_ctrl_handler_setup(imx708->subdev.ctrl_handler); ++ if (ret) ++ return ret; ++ ++ /* set stream on register */ ++ return imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, ++ IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); ++} ++ ++/* Stop streaming */ ++static void imx708_stop_streaming(struct imx708 *imx708) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ int ret; ++ ++ /* set stream off register */ ++ ret = imx708_write_reg(imx708, IMX708_REG_MODE_SELECT, ++ IMX708_REG_VALUE_08BIT, IMX708_MODE_STANDBY); ++ if (ret) ++ dev_err(&client->dev, "%s failed to set stream\n", __func__); ++} ++ ++static int imx708_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ mutex_lock(&imx708->mutex); ++ if (imx708->streaming == enable) { ++ mutex_unlock(&imx708->mutex); ++ return 0; ++ } ++ ++ if (enable) { ++ ret = pm_runtime_get_sync(&client->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(&client->dev); ++ goto err_unlock; ++ } ++ ++ /* ++ * Apply default & customized values ++ * and then start streaming. ++ */ ++ ret = imx708_start_streaming(imx708); ++ if (ret) ++ goto err_rpm_put; ++ } else { ++ imx708_stop_streaming(imx708); ++ pm_runtime_put(&client->dev); ++ } ++ ++ imx708->streaming = enable; ++ ++ /* vflip/hflip and hdr mode cannot change during streaming */ ++ __v4l2_ctrl_grab(imx708->vflip, enable); ++ __v4l2_ctrl_grab(imx708->hflip, enable); ++ ++ mutex_unlock(&imx708->mutex); ++ ++ return ret; ++ ++err_rpm_put: ++ pm_runtime_put(&client->dev); ++err_unlock: ++ mutex_unlock(&imx708->mutex); ++ ++ return ret; ++} ++ ++/* Power/clock management functions */ ++static int imx708_power_on(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx708 *imx708 = to_imx708(sd); ++ int ret; ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(imx708_supply_name), ++ imx708->supplies); ++ if (ret) { ++ dev_err(&client->dev, "%s: failed to enable regulators\n", ++ __func__); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(imx708->inclk); ++ if (ret) { ++ dev_err(&client->dev, "%s: failed to enable clock\n", ++ __func__); ++ goto reg_off; ++ } ++ ++ gpiod_direction_output(imx708->reset_gpio, 1); ++ usleep_range(IMX708_XCLR_MIN_DELAY_US, ++ IMX708_XCLR_MIN_DELAY_US + IMX708_XCLR_DELAY_RANGE_US); ++ ++ v4l2_dbg(1, debug, &imx708->subdev,"%s.\n", __func__); ++ ++ ++ return 0; ++ ++reg_off: ++ regulator_bulk_disable(ARRAY_SIZE(imx708_supply_name), ++ imx708->supplies); ++ return ret; ++} ++ ++static int imx708_power_off(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx708 *imx708 = to_imx708(sd); ++ ++ gpiod_direction_output(imx708->reset_gpio, 0); ++ regulator_bulk_disable(ARRAY_SIZE(imx708_supply_name), ++ imx708->supplies); ++ clk_disable_unprepare(imx708->inclk); ++ ++ /* Force reprogramming of the common registers when powered up again. */ ++ imx708->common_regs_written = false; ++ ++ v4l2_dbg(1, debug, &imx708->subdev,"%s.\n", __func__); ++ ++ return 0; ++} ++ ++static int __maybe_unused imx708_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx708 *imx708 = to_imx708(sd); ++ ++ if (imx708->streaming) ++ imx708_stop_streaming(imx708); ++ ++ return 0; ++} ++ ++static int __maybe_unused imx708_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx708 *imx708 = to_imx708(sd); ++ int ret; ++ ++ if (imx708->streaming) { ++ ret = imx708_start_streaming(imx708); ++ if (ret) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ imx708_stop_streaming(imx708); ++ imx708->streaming = 0; ++ return ret; ++} ++ ++static int imx708_get_regulators(struct imx708 *imx708) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(imx708_supply_name); i++) ++ imx708->supplies[i].supply = imx708_supply_name[i]; ++ ++ return devm_regulator_bulk_get(&client->dev, ++ ARRAY_SIZE(imx708_supply_name), ++ imx708->supplies); ++} ++ ++/* Verify chip ID */ ++static int imx708_identify_module(struct imx708 *imx708) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ int ret; ++ u32 val; ++ ++ ret = imx708_read_reg(imx708, IMX708_REG_CHIP_ID, ++ IMX708_REG_VALUE_16BIT, &val); ++ if (ret) { ++ dev_err(&client->dev, "failed to read chip id %x, with error %d\n", ++ IMX708_CHIP_ID, ret); ++ return ret; ++ } ++ ++ if (val != IMX708_CHIP_ID) { ++ dev_err(&client->dev, "chip id mismatch: %x!=%x\n", ++ IMX708_CHIP_ID, val); ++ return -EIO; ++ } ++ ++ ret = imx708_read_reg(imx708, 0x0000, IMX708_REG_VALUE_16BIT, &val); ++ if (!ret) { ++ dev_info(&client->dev, "camera module ID 0x%04x\n", val); ++ snprintf(imx708->subdev.name, sizeof(imx708->subdev.name), "imx708%s%s", ++ val & 0x02 ? "_wide" : "", ++ val & 0x80 ? "_noir" : ""); ++ } ++ ++ return 0; ++} ++ ++static int imx708_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ struct i2c_client *client = imx708->client; ++ int ret = 0; ++ ++ mutex_lock(&imx708->mutex); ++ ++ if (imx708->power_on == !!on) ++ goto unlock_and_return; ++ ++ if (on) { ++ ret = pm_runtime_get_sync(&client->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(&client->dev); ++ goto unlock_and_return; ++ } ++ imx708->power_on = true; ++ } else { ++ pm_runtime_put(&client->dev); ++ imx708->power_on = false; ++ } ++ v4l2_dbg(1, debug, &imx708->subdev,"%s: %d.\n", __func__, on); ++unlock_and_return: ++ mutex_unlock(&imx708->mutex); ++ ++ return ret; ++} ++ ++ ++static int imx708_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, ++ struct v4l2_mbus_config *config) ++{ ++ // struct imx708 *imx708 = to_imx708(sd); ++ // const struct imx708_mode *mode = imx708->cur_mode; ++ ++ u32 val = 0; ++ ++ val = 1 << 1/*(imx708->lanes - 1)*/| ++ V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ // if (mode->hdr_mode != NO_HDR) ++ // val |= V4L2_MBUS_CSI2_CHANNEL_1; ++ ++ config->type = V4L2_MBUS_CSI2_DPHY; ++ config->flags = val; ++ ++ return 0; ++} ++ ++static void imx708_get_module_inf(struct imx708 *imx708, ++ struct rkmodule_inf *inf) ++{ ++ memset(inf, 0, sizeof(*inf)); ++ strlcpy(inf->base.sensor, IMX708_NAME, sizeof(inf->base.sensor)); ++ strlcpy(inf->base.module, imx708->module_name, ++ sizeof(inf->base.module)); ++ strlcpy(inf->base.lens, imx708->len_name, sizeof(inf->base.lens)); ++ ++ v4l2_dbg(1, debug, &imx708->subdev,"%s: get_module_inf:%s, %s, %s.\n", __func__, ++ inf->base.sensor, inf->base.module, inf->base.lens); ++} ++ ++static long imx708_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ struct rkmodule_hdr_cfg *hdr; ++ u32 i, h, w; ++ long ret = 0; ++ // struct rkmodule_csi_dphy_param *dphy_param; ++ ++ switch (cmd) ++ { ++ case RKMODULE_GET_MODULE_INFO: ++ imx708_get_module_inf(imx708, (struct rkmodule_inf *)arg); ++ break; ++ // case RKMODULE_GET_HDR_CFG: ++ // hdr = (struct rkmodule_hdr_cfg *)arg; ++ // if (imx708->cur_mode->hdr_mode == NO_HDR) ++ // hdr->esp.mode = HDR_NORMAL_VC; ++ // else ++ // hdr->esp.mode = HDR_ID_CODE; ++ // hdr->hdr_mode = imx708->cur_mode->hdr_mode; ++ // break; ++ case RKMODULE_SET_HDR_CFG: ++ hdr = (struct rkmodule_hdr_cfg *)arg; ++ w = imx708->cur_mode->width; ++ h = imx708->cur_mode->height; ++ for (i = 0; i < imx708->cfg_num; i++) { ++ if (w == supported_modes[i].width && ++ h == supported_modes[i].height && ++ supported_modes[i].hdr_mode == hdr->hdr_mode) { ++ imx708->cur_mode = &supported_modes[i]; ++ break; ++ } ++ } ++ if (i == imx708->cfg_num) { ++ dev_err(&imx708->client->dev, ++ "not find hdr mode:%d %dx%d config\n", ++ hdr->hdr_mode, w, h); ++ ret = -EINVAL; ++ } else { ++ imx708_set_framing_limits(imx708); ++ } ++ break; ++ // case RKMODULE_GET_CSI_DPHY_PARAM: ++ // if (imx708->cur_mode->hdr_mode == HDR_X2) { ++ // dphy_param = (struct rkmodule_csi_dphy_param *)arg; ++ // if (dphy_param->vendor == dcphy_param.vendor) ++ // *dphy_param = dcphy_param; ++ // dev_info(&imx708->client->dev, ++ // "get sensor dphy param\n"); ++ // } else ++ // ret = -EINVAL; ++ // break; ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ return ret; ++} ++ ++#ifdef CONFIG_COMPAT ++static long imx708_compat_ioctl32(struct v4l2_subdev *sd, ++ unsigned int cmd, unsigned long arg) ++{ ++ void __user *up = compat_ptr(arg); ++ struct rkmodule_inf *inf; ++ struct rkmodule_awb_cfg *cfg; ++ struct rkmodule_hdr_cfg *hdr; ++ long ret; ++ // struct rkmodule_csi_dphy_param *dphy_param; ++ ++ switch (cmd) { ++ case RKMODULE_GET_MODULE_INFO: ++ inf = kzalloc(sizeof(*inf), GFP_KERNEL); ++ if (!inf) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ ret = imx708_ioctl(sd, cmd, inf); ++ if (!ret) { ++ if (copy_to_user(up, inf, sizeof(*inf))) { ++ kfree(inf); ++ return -EFAULT; ++ } ++ } ++ kfree(inf); ++ break; ++ case RKMODULE_AWB_CFG: ++ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); ++ if (!cfg) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ if (copy_from_user(cfg, up, sizeof(*cfg))) { ++ kfree(cfg); ++ return -EFAULT; ++ } ++ ret = imx708_ioctl(sd, cmd, cfg); ++ kfree(cfg); ++ break; ++ case RKMODULE_GET_HDR_CFG: ++ hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); ++ if (!hdr) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ ret = imx708_ioctl(sd, cmd, hdr); ++ if (!ret) { ++ if (copy_to_user(up, hdr, sizeof(*hdr))) { ++ kfree(hdr); ++ return -EFAULT; ++ } ++ } ++ kfree(hdr); ++ break; ++ case RKMODULE_SET_HDR_CFG: ++ hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); ++ if (!hdr) { ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ if (copy_from_user(hdr, up, sizeof(*hdr))) { ++ kfree(hdr); ++ return -EFAULT; ++ } ++ ret = imx708_ioctl(sd, cmd, hdr); ++ kfree(hdr); ++ break; ++ // case RKMODULE_GET_CSI_DPHY_PARAM: ++ // dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); ++ // if (!dphy_param) { ++ // ret = -ENOMEM; ++ // return ret; ++ // } ++ ++ // ret = imx708_ioctl(sd, cmd, dphy_param); ++ // if (!ret) { ++ // ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); ++ // if (ret) ++ // ret = -EFAULT; ++ // } ++ // kfree(dphy_param); ++ // break; ++ ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ return ret; ++ ++} ++#endif ++ ++static int imx708_enum_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_pad_config *cfg, ++ struct v4l2_subdev_frame_interval_enum *fie) ++{ ++ struct imx708 *imx708 = to_imx708(sd); ++ ++ if (fie->index >= imx708->cfg_num) ++ return -EINVAL; ++ ++ fie->code = supported_modes[fie->index].bus_fmt; ++ fie->width = supported_modes[fie->index].width; ++ fie->height = supported_modes[fie->index].height; ++ fie->interval = supported_modes[fie->index].max_fps; ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops imx708_core_ops = { ++ .s_power = imx708_s_power, ++ .ioctl = imx708_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl32 = imx708_compat_ioctl32, ++#endif ++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event, ++ .unsubscribe_event = v4l2_event_subdev_unsubscribe, ++}; ++ ++static const struct v4l2_subdev_video_ops imx708_video_ops = { ++ .s_stream = imx708_set_stream, ++ .g_frame_interval = imx708_g_frame_interval, ++}; ++ ++static const struct v4l2_subdev_pad_ops imx708_pad_ops = { ++ .enum_mbus_code = imx708_enum_mbus_code, ++ .enum_frame_size = imx708_enum_frame_size, ++ .enum_frame_interval = imx708_enum_frame_interval, ++ .get_fmt = imx708_get_pad_format, ++ .set_fmt = imx708_set_pad_format, ++ // .get_selection = imx708_get_selection, ++ .set_mbus_config = imx708_g_mbus_config, ++ ++}; ++ ++static const struct v4l2_subdev_ops imx708_subdev_ops = { ++ .core = &imx708_core_ops, ++ .video = &imx708_video_ops, ++ .pad = &imx708_pad_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops imx708_internal_ops = { ++ .open = imx708_open, ++}; ++ ++/* Initialize control handlers */ ++static int imx708_init_controls(struct imx708 *imx708) ++{ ++ struct v4l2_ctrl_handler *ctrl_hdlr; ++ struct i2c_client *client = v4l2_get_subdevdata(&imx708->subdev); ++ struct v4l2_fwnode_device_properties props; ++ struct v4l2_ctrl *ctrl; ++ unsigned int i; ++ int ret; ++ ++ ctrl_hdlr = &imx708->ctrl_handler; ++ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); ++ if (ret) ++ return ret; ++ ++ mutex_init(&imx708->mutex); ++ ctrl_hdlr->lock = &imx708->mutex; ++ ++ /* By default, PIXEL_RATE is read only */ ++ imx708->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, ++ IMX708_INITIAL_PIXEL_RATE, ++ IMX708_INITIAL_PIXEL_RATE, 1, ++ IMX708_INITIAL_PIXEL_RATE); ++ ++ ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_LINK_FREQ, 0, 0, ++ &link_freqs[imx708->link_freq_idx]); ++ if (ctrl) ++ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ ++ /* ++ * Create the controls here, but mode specific limits are setup ++ * in the imx708_set_framing_limits() call below. ++ */ ++ imx708->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_VBLANK, 0, 0xffff, 1, 0); ++ imx708->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_HBLANK, 0, 0xffff, 1, 0); ++ ++ imx708->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_EXPOSURE, ++ IMX708_EXPOSURE_MIN, ++ IMX708_EXPOSURE_MAX, ++ IMX708_EXPOSURE_STEP, ++ IMX708_EXPOSURE_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, ++ IMX708_ANA_GAIN_MIN, IMX708_ANA_GAIN_MAX, ++ IMX708_ANA_GAIN_STEP, IMX708_ANA_GAIN_DEFAULT); ++ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, V4L2_CID_DIGITAL_GAIN, ++ IMX708_DGTL_GAIN_MIN, IMX708_DGTL_GAIN_MAX, ++ IMX708_DGTL_GAIN_STEP, IMX708_DGTL_GAIN_DEFAULT); ++ ++ imx708->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ ++ imx708->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_cluster(2, &imx708->hflip); ++ ++ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(imx708_test_pattern_menu) - 1, ++ 0, 0, imx708_test_pattern_menu); ++ for (i = 0; i < 4; i++) { ++ /* ++ * The assumption is that ++ * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 ++ * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 ++ * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 ++ */ ++ v4l2_ctrl_new_std(ctrl_hdlr, &imx708_ctrl_ops, ++ V4L2_CID_TEST_PATTERN_RED + i, ++ IMX708_TEST_PATTERN_COLOUR_MIN, ++ IMX708_TEST_PATTERN_COLOUR_MAX, ++ IMX708_TEST_PATTERN_COLOUR_STEP, ++ IMX708_TEST_PATTERN_COLOUR_MAX); ++ /* The "Solid color" pattern is white by default */ ++ } ++ ++ ret = v4l2_fwnode_device_parse(&client->dev, &props); ++ if (ret) ++ goto error; ++ ++ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx708_ctrl_ops, &props); ++ ++ if (ctrl_hdlr->error) { ++ ret = ctrl_hdlr->error; ++ dev_err(&client->dev, "%s control init failed (%d)\n", ++ __func__, ret); ++ goto error; ++ } ++ ++ imx708->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ imx708->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ imx708->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ++ ++ imx708->subdev.ctrl_handler = ctrl_hdlr; ++ ++ /* Setup exposure and frame/line length limits. */ ++ imx708_set_framing_limits(imx708); ++ ++ return 0; ++ ++error: ++ v4l2_ctrl_handler_free(ctrl_hdlr); ++ mutex_destroy(&imx708->mutex); ++ ++ return ret; ++} ++ ++static int imx708_check_hwcfg(struct device *dev, struct imx708 *imx708) ++{ ++ struct fwnode_handle *endpoint; ++ struct v4l2_fwnode_endpoint ep_cfg = { ++ .bus_type = V4L2_MBUS_CSI2_DPHY ++ }; ++ int ret = -EINVAL; ++ int i; ++ ++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); ++ if (!endpoint) { ++ dev_err(dev, "endpoint node not found\n"); ++ return -EINVAL; ++ } ++ ++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { ++ dev_err(dev, "could not parse endpoint\n"); ++ goto error_out; ++ } ++ ++ /* Check the number of MIPI CSI2 data lanes */ ++ if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { ++ dev_err(dev, "only 2 data lanes are currently supported\n"); ++ goto error_out; ++ } ++ ++ /* Check the link frequency set in device tree */ ++ if (!ep_cfg.nr_of_link_frequencies) { ++ dev_err(dev, "link-frequency property not found in DT\n"); ++ goto error_out; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(link_freqs); i++) { ++ if (link_freqs[i] == ep_cfg.link_frequencies[0]) { ++ imx708->link_freq_idx = i; ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(link_freqs)) { ++ dev_err(dev, "Link frequency not supported: %lld\n", ++ ep_cfg.link_frequencies[0]); ++ ret = -EINVAL; ++ goto error_out; ++ } ++ ++ ret = 0; ++ ++error_out: ++ v4l2_fwnode_endpoint_free(&ep_cfg); ++ fwnode_handle_put(endpoint); ++ ++ return ret; ++} ++ ++static int imx708_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ struct device_node *node = dev->of_node; ++ struct imx708 *imx708; ++ struct v4l2_subdev *sd; ++ char facing[2]; ++ int ret; ++ u32 i, hdr_mode = 0; ++ ++ dev_info(dev, "driver version: %02x.%02x.%02x", ++ DRIVER_VERSION >> 16, ++ (DRIVER_VERSION & 0xff00) >> 8, ++ DRIVER_VERSION & 0x00ff); ++ ++ imx708 = devm_kzalloc(&client->dev, sizeof(*imx708), GFP_KERNEL); ++ if (!imx708) ++ return -ENOMEM; ++ ++ ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, ++ &imx708->module_index); ++ ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, ++ &imx708->module_facing); ++ ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, ++ &imx708->module_name); ++ ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, ++ &imx708->len_name); ++ if (ret) { ++ dev_err(dev, "could not get module information!\n"); ++ return -EINVAL; ++ } ++ ++ ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); ++ if (ret) { ++ hdr_mode = NO_HDR; ++ dev_warn(dev, " Get hdr mode failed! no hdr default\n"); ++ } ++ ++ imx708->client = client; ++ imx708->cfg_num = ARRAY_SIZE(supported_modes); ++ for (i = 0; i < imx708->cfg_num; i++) { ++ if (hdr_mode == supported_modes[i].hdr_mode) { ++ imx708->cur_mode = &supported_modes[i]; ++ break; ++ } ++ } ++ ++ if (i >= imx708->cfg_num) { ++ dev_warn(dev, " Get hdr mode failed! no hdr config\n"); ++ imx708->cur_mode = &supported_modes[0]; ++ } ++ ++ sd = &imx708->subdev; ++ v4l2_i2c_subdev_init(sd, client, &imx708_subdev_ops); ++ ++ /* Check the hardware configuration in device tree */ ++ if (imx708_check_hwcfg(dev, imx708)) ++ return -EINVAL; ++ ++ /* Get system clock (inclk) */ ++ imx708->inclk = devm_clk_get(dev, "xclk"); ++ if (IS_ERR(imx708->inclk)) ++ return dev_err_probe(dev, PTR_ERR(imx708->inclk), ++ "failed to get xclk\n"); ++ ++ imx708->inclk_freq = clk_get_rate(imx708->inclk); ++ if (imx708->inclk_freq != IMX708_INCLK_FREQ) ++ return dev_err_probe(dev, -EINVAL, ++ "inclk frequency not supported: %d Hz\n", ++ imx708->inclk_freq); ++ ++ ret = imx708_get_regulators(imx708); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to get regulators\n"); ++ ++ /* Request optional enable pin */ ++ imx708->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); ++ if (IS_ERR(imx708->reset_gpio)) ++ dev_warn(dev, "Failed to get reset-gpios\n"); ++ /* ++ * The sensor must be powered for imx708_identify_module() ++ * to be able to read the CHIP_ID register ++ */ ++ ret = imx708_power_on(dev); ++ if (ret) ++ return ret; ++ ++ ret = imx708_identify_module(imx708); ++ if (ret) ++ goto error_power_off; ++ ++ ++ /* Enable runtime PM and turn off the device */ ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ pm_runtime_idle(dev); ++ ++ /* This needs the pm runtime to be registered. */ ++ ret = imx708_init_controls(imx708); ++ if (ret) ++ goto error_pm_runtime; ++#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API ++ /* Initialize subdev */ ++ sd->internal_ops = &imx708_internal_ops; ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | ++ V4L2_SUBDEV_FL_HAS_EVENTS; ++#endif ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ /* Initialize source pads */ ++ imx708->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ ++ ret = media_entity_pads_init(&sd->entity, 1, &imx708->pad); ++ if (ret) { ++ dev_err(dev, "failed to init entity pads: %d\n", ret); ++ goto error_handler_free; ++ } ++#endif ++ ++ memset(facing, 0, sizeof(facing)); ++ if (strcmp(imx708->module_facing, "back") == 0) ++ facing[0] = 'b'; ++ else ++ facing[0] = 'f'; ++ ++ snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", ++ imx708->module_index, facing, ++ IMX708_NAME, dev_name(sd->dev)); ++ ++ ret = v4l2_async_register_subdev_sensor_common(&imx708->subdev); ++ if (ret < 0) { ++ dev_err(dev, "failed to register sensor sub-device: %d\n", ret); ++ goto error_media_entity; ++ } ++ ++ return 0; ++ ++error_media_entity: ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ media_entity_cleanup(&sd->entity); ++#endif ++ ++error_handler_free: ++ v4l2_ctrl_handler_free(&imx708->ctrl_handler); ++ mutex_destroy(&imx708->mutex); ++error_pm_runtime: ++ pm_runtime_disable(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ ++error_power_off: ++ imx708_power_off(&client->dev); ++ ++ return ret; ++} ++ ++static int imx708_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct imx708 *imx708 = to_imx708(sd); ++ ++ v4l2_async_unregister_subdev(sd); ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ media_entity_cleanup(&sd->entity); ++#endif ++ v4l2_ctrl_handler_free(&imx708->ctrl_handler); ++ mutex_destroy(&imx708->mutex); ++ ++ pm_runtime_disable(&client->dev); ++ if (!pm_runtime_status_suspended(&client->dev)) ++ imx708_power_off(&client->dev); ++ pm_runtime_set_suspended(&client->dev); ++ return 0; ++} ++ ++#if IS_ENABLED(CONFIG_OF) ++static const struct of_device_id imx708_of_match[] = { ++ { .compatible = "sony,imx708" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, imx708_of_match); ++#endif ++ ++static const struct i2c_device_id imx708_match_id[] = { ++ { "sony,imx708", 0 }, ++ { }, ++}; ++ ++static const struct dev_pm_ops imx708_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(imx708_suspend, imx708_resume) ++ SET_RUNTIME_PM_OPS(imx708_power_off, imx708_power_on, NULL) ++}; ++ ++static struct i2c_driver imx708_i2c_driver = { ++ .driver = { ++ .name = "imx708", ++ .of_match_table = of_match_ptr(imx708_of_match), ++ .pm = &imx708_pm_ops, ++ }, ++ .probe = &imx708_probe, ++ .remove = &imx708_remove, ++ .id_table = imx708_match_id, ++}; ++ ++static int __init sensor_mod_init(void) ++{ ++ return i2c_add_driver(&imx708_i2c_driver); ++} ++ ++static void __exit sensor_mod_exit(void) ++{ ++ i2c_del_driver(&imx708_i2c_driver); ++} ++ ++device_initcall_sync(sensor_mod_init); ++module_exit(sensor_mod_exit); ++ ++MODULE_DESCRIPTION("Sony IMX708 sensor driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/scripts/patches/openhd_cm3.patch b/scripts/patches/openhd_cm3.patch new file mode 100644 index 0000000000000..85a48f6af3ef4 --- /dev/null +++ b/scripts/patches/openhd_cm3.patch @@ -0,0 +1,21 @@ +commit bbb8fcc59c704fd924891466f62bed04b69e2aa5 +Author: Raphael <68374617+raphaelscholle@users.noreply.github.com> +Date: Fri Apr 12 22:32:55 2024 +0200 + + Update rk3566-radxa-cm3-io.dts + +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts +index fb73884b401e..c634e2065fce 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3-io.dts +@@ -191,7 +191,9 @@ &u2phy0_host { + }; + + &usbhost_dwc3 { +- status = "okay"; ++ dr_mode = "host"; ++ extcon = <&usb2phy0>; ++ status = "okay"; + }; + + &usbhost30 { diff --git a/scripts/patches/openhd_drm-rock5.patch b/scripts/patches/openhd_drm-rock5.patch new file mode 100644 index 0000000000000..79893d2516741 --- /dev/null +++ b/scripts/patches/openhd_drm-rock5.patch @@ -0,0 +1,31 @@ +commit d63d3764aa9d4ccb62d64c5c1d3ee0118d23bf09 +Author: Raphael Scholle +Date: Fri Mar 15 19:20:58 2024 +0100 + + add openhd DRM patches + Co-authored-by: Consti10 + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +index f86109b5420a..950ffd54aa8e 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +@@ -5375,7 +5375,11 @@ rockchip_atomic_helper_update_plane(struct drm_plane *plane, + if (plane == crtc->cursor || vpstate->async_commit) + state->legacy_cursor_update = true; + ++ // Consti10 ++ state->legacy_cursor_update = true; ++ //ret = drm_atomic_nonblocking_commit(state); + ret = drm_atomic_commit(state); ++ ret=0; + fail: + drm_atomic_state_put(state); + return ret; +@@ -9726,6 +9730,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state + */ + vop2_wait_for_irq_handler(crtc); + ++ + /** + * move here is to make sure current fs call function is complete, + * so when layer_sel_update is true, we can skip current vblank correctly. diff --git a/scripts/patches/openhd_i2c.patch b/scripts/patches/openhd_i2c.patch new file mode 100644 index 0000000000000..742ba4707c60a --- /dev/null +++ b/scripts/patches/openhd_i2c.patch @@ -0,0 +1,38 @@ +commit b0988eb0223230690e413a6250eaceba1226cdee +Author: Raphael <68374617+raphaelscholle@users.noreply.github.com> +Date: Mon Jun 17 02:09:52 2024 +0200 + + i2c-patch + +diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +index 62b0e763db2a..9b71b7abd50f 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b.dts +@@ -323,6 +323,11 @@ regulator-state-mem { + }; + }; + ++&i2c7 { ++ status = "okay"; ++ pinctrl-0 = <&i2c7m3_xfer>; ++}; ++ + &dmc { + center-supply = <&vdd_ddr_s0>; + mem-supply = <&vdd_log_s0>; +diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +index ebed3da7a556..e85316332fb0 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3588s-rock-5a.dts +@@ -211,6 +211,11 @@ regulator-state-mem { + }; + }; + ++&i2c8 { ++ status = "okay"; ++ pinctrl-0 = <&i2c8m2_xfer>; ++}; ++ + &i2c2 { + status = "okay"; + pinctrl-names = "default"; diff --git a/scripts/patches/openhd_keyboard.patch b/scripts/patches/openhd_keyboard.patch new file mode 100644 index 0000000000000..4dc4c2082b10a --- /dev/null +++ b/scripts/patches/openhd_keyboard.patch @@ -0,0 +1,1329 @@ +commit b23eee91326342faf34caf03eed70e0ef9c098f1 +Author: Raphael Scholle +Date: Sat Mar 16 00:58:37 2024 +0100 + + add openhd keyboard + +# diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi +# index 0c83b9fb3..d92bdd746 100644 +# --- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi +# +++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi +# @@ -83,6 +83,48 @@ sdio_pwrseq: sdio-pwrseq { +# */ +# reset-gpios = <&gpio2 RK_PB7 GPIO_ACTIVE_LOW>; +# }; +# + +# + keys: gpio-keys { +# + compatible = "gpio-keys"; +# + pinctrl-names = "default"; +# + pinctrl-0 = <&pwr_key>; +# + autorepeat; +# + up-key { +# + gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>; +# + linux,code = <103>; +# + label = "key up"; +# + }; +# + down-key { +# + gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>; +# + linux,code = <108>; +# + label = "key down"; +# + }; +# + left-key { +# + gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_LOW>; +# + linux,code = <106>; +# + label = "key left"; +# + }; +# + right-key { +# + gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; +# + linux,code = <107>; +# + label = "key right"; +# + }; +# + enter-key { +# + gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>; +# + linux,code = <29>; +# + label = "key enter"; +# + }; +# + back-key { +# + gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>; +# + linux,code = <159>; +# + label = "key back"; +# + }; +# + reset-key { +# + gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>; +# + linux,code = <2>; +# + label = "key reset"; +# + }; +# + }; +# }; + +# &cpu0 { +# @@ -188,6 +230,19 @@ pwrkey { +# status = "okay"; +# }; + +# + keys { +# + pwr_key: pwr-key { +# + rockchip,pins = +# + <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>, +# + <4 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>, +# + <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>, +# + <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>, +# + <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>, +# + <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>, +# + <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; +# + }; +# + }; +# + +# pinctrl_rk8xx: pinctrl_rk8xx { +# gpio-controller; +# #gpio-cells = <2>; +# @@ -684,7 +739,7 @@ brcmf: wifi@0 { +# compatible = "brcm,bcm4329-fmac"; +# reg = <1>; +# interrupt-parent = <&gpio2>; +# - interrupts = ; +# + // interrupts = ; +# interrupt-names = "host-wake"; +# pinctrl-names = "default"; +# pinctrl-0 = <&wifi_host_wake_l>; +# @@ -746,7 +801,7 @@ wifi_enable_h: wifi-enable-h { +# }; + +# wifi_host_wake_l: wifi-host-wake-irq { +# - rockchip,pins = <2 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; +# + // rockchip,pins = <2 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; +# }; +# }; + +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts +index e11ba6772..cb42385aa 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts +@@ -26,7 +26,7 @@ wireless_bluetooth: wireless-bluetooth { + pinctrl-names = "default", "rts_gpio"; + pinctrl-0 = <&uart1m0_rtsn>; + pinctrl-1 = <&uart1_gpios>; +- BT,reset_gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; ++ // BT,reset_gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; + BT,wake_gpio = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; + BT,wake_host_irq = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; + status = "okay"; +@@ -39,6 +39,48 @@ sdio_pwrseq: sdio-pwrseq { + reset-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_LOW>; + }; + ++ keys: gpio-keys { ++ compatible = "gpio-keys"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwr_key>; ++ autorepeat; ++ up-key { ++ gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>; ++ linux,code = <103>; ++ label = "key up"; ++ }; ++ down-key { ++ gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>; ++ linux,code = <108>; ++ label = "key down"; ++ }; ++ left-key { ++ gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_LOW>; ++ linux,code = <106>; ++ label = "key left"; ++ }; ++ right-key { ++ gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; ++ linux,code = <107>; ++ label = "key right"; ++ }; ++ enter-key { ++ gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>; ++ linux,code = <29>; ++ label = "key enter"; ++ }; ++ back-key { ++ gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>; ++ linux,code = <159>; ++ label = "key back"; ++ }; ++ reset-key { ++ gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>; ++ linux,code = <2>; ++ label = "key reset"; ++ }; ++ }; ++ + board_antenna: board-antenna { + status = "okay"; + compatible = "regulator-fixed"; +@@ -134,6 +176,19 @@ sdmmc1_pwren: sdmmc1-pwren { + }; + }; + ++ keys { ++ pwr_key: pwr-key { ++ rockchip,pins = ++ <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>, ++ <4 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>, ++ <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++ + antenna { + ant_1: ant-1 { + rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>; + +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts +index 0abc6e60db50..adce697c8c45 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts +@@ -5,153 +5,994 @@ + * + */ + +-/dts-v1/; ++ /dts-v1/; + +-#include "rk3566-radxa-zero3.dtsi" +- +-/ { +- model = "Radxa ZERO 3"; +- compatible ="radxa,zero3w-ap6212", "radxa,zero3", "rockchip,rk3566"; +- +- wireless_wlan: wireless-wlan { +- compatible = "wlan-platdata"; +- wifi_chip_type = "ap6212"; +- pinctrl-names = "default"; +- pinctrl-0 = <&wifi_host_wake_irq>; +- WIFI,host_wake_irq = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; +- status = "okay"; +- }; +- +- sdio_pwrseq: sdio-pwrseq { +- compatible = "mmc-pwrseq-simple"; +- pinctrl-names = "default"; +- pinctrl-0 = <&wifi_enable_h>; +- reset-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_LOW>; +- }; +- +- board_antenna: board-antenna { +- status = "okay"; +- compatible = "regulator-fixed"; +- enable-active-low; +- gpio = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>; +- regulator-always-on; +- regulator-boot-on; +- pinctrl-0 = <&ant_1>; +- pinctrl-names = "default"; +- regulator-name = "board_antenna"; +- }; +-}; +- +-&uart1 { +- status = "okay"; +- pinctrl-names = "default"; +- pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn &uart1m0_rtsn>; +- +- bluetooth { +- compatible = "brcm,bcm43438-bt"; +- clocks = <&rk817 1>; +- clock-names = "lpo"; +- device-wakeup-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; +- host-wakeup-gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; +- shutdown-gpios = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; +- max-speed = <1500000>; +- pinctrl-names = "default"; +- pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>; +- vbat-supply = <&vcc3v3_sys>; +- vddio-supply = <&vcc_1v8>; +- }; +-}; +- +-&sdmmc1 { +- supports-sdio; +- bus-width = <4>; +- disable-wp; +- cap-sd-highspeed; +- cap-sdio-irq; +- keep-power-in-suspend; +- non-removable; +- rockchip,default-sample-phase = <180>; +- mmc-pwrseq = <&sdio_pwrseq>; +- non-removable; +- no-sd; +- no-mmc; +- pinctrl-names = "default"; +- pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>; +- sd-uhs-sdr104; +- status = "okay"; +-}; +- +-&pinctrl { +- wifi { +- wifi_enable_h: wifi-enable-h { +- rockchip,pins = <0 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; +- }; +- +- wifi_host_wake_irq: wifi-host-wake-irq { +- rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_down>; +- }; +- }; +- +- bt { +- bt_enable_h: bt-enable-h { +- rockchip,pins = <0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; +- }; +- +- bt_host_wake_l: bt-host-wake-l { +- rockchip,pins = <0 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; +- }; +- +- bt_wake_l: bt-wake-l { +- rockchip,pins = <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; +- }; +- }; +- +- sdmmc1 { +- /omit-if-no-ref/ +- sdmmc1_bus4: sdmmc1-bus4 { +- rockchip,pins = +- /* sdmmc1_d0 */ +- <2 RK_PA3 1 &pcfg_pull_up_drv_level_12>, +- /* sdmmc1_d1 */ +- <2 RK_PA4 1 &pcfg_pull_up_drv_level_12>, +- /* sdmmc1_d2 */ +- <2 RK_PA5 1 &pcfg_pull_up_drv_level_12>, +- /* sdmmc1_d3 */ +- <2 RK_PA6 1 &pcfg_pull_up_drv_level_12>; +- }; +- +- /omit-if-no-ref/ +- sdmmc1_clk: sdmmc1-clk { +- rockchip,pins = +- /* sdmmc1_clk */ +- <2 RK_PB0 1 &pcfg_pull_up_drv_level_12>; +- }; +- +- /omit-if-no-ref/ +- sdmmc1_cmd: sdmmc1-cmd { +- rockchip,pins = +- /* sdmmc1_cmd */ +- <2 RK_PA7 1 &pcfg_pull_up_drv_level_12>; +- }; +- +- /omit-if-no-ref/ +- sdmmc1_det: sdmmc1-det { +- rockchip,pins = +- /* sdmmc1_det */ +- <2 RK_PB2 1 &pcfg_pull_up>; +- }; +- +- /omit-if-no-ref/ +- sdmmc1_pwren: sdmmc1-pwren { +- rockchip,pins = +- /* sdmmc1_pwren */ +- <2 RK_PB1 1 &pcfg_pull_none>; +- }; +- }; +- +- antenna { +- ant_1: ant-1 { +- rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>; +- }; +- }; +-}; ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include ++ #include "rk3566.dtsi" ++ ++ / { ++ model = "Radxa ZERO 3"; ++ compatible = "radxa,zero3", "rockchip,rk3566"; ++ ++ fiq_debugger: fiq-debugger { ++ compatible = "rockchip,fiq-debugger"; ++ rockchip,serial-id = <2>; ++ rockchip,wake-irq = <0>; ++ /* If enable uart uses irq instead of fiq */ ++ rockchip,irq-mode-enable = <1>; ++ rockchip,baudrate = <1500000>; /* Only 115200 and 1500000 */ ++ interrupts = ; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2m0_xfer>; ++ status = "okay"; ++ }; ++ ++ debug: debug@fd904000 { ++ compatible = "rockchip,debug"; ++ reg = <0x0 0xfd904000 0x0 0x1000>, ++ <0x0 0xfd905000 0x0 0x1000>, ++ <0x0 0xfd906000 0x0 0x1000>, ++ <0x0 0xfd907000 0x0 0x1000>; ++ }; ++ ++ cspmu: cspmu@fd90c000 { ++ compatible = "rockchip,cspmu"; ++ reg = <0x0 0xfd90c000 0x0 0x1000>, ++ <0x0 0xfd90d000 0x0 0x1000>, ++ <0x0 0xfd90e000 0x0 0x1000>, ++ <0x0 0xfd90f000 0x0 0x1000>; ++ }; ++ ++ vcc_sys: vcc-sys { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ }; ++ ++ vcc3v3_sys: vcc3v3-sys { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc3v3_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ vin-supply = <&vcc_sys>; ++ }; ++ ++ vcc5v0_sys: vcc5v0-sys { ++ compatible = "regulator-fixed"; ++ regulator-name = "vcc5v0_sys"; ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ vin-supply = <&vcc_sys>; ++ }; ++ ++ wireless_wlan: wireless-wlan { ++ compatible = "wlan-platdata"; ++ wifi_chip_type = "ap6212"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&wifi_host_wake_irq>; ++ WIFI,host_wake_irq = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; ++ status = "okay"; ++ }; ++ ++ sdio_pwrseq: sdio-pwrseq { ++ compatible = "mmc-pwrseq-simple"; ++ clocks = <&rk817 1>; ++ clock-names = "ext_clock"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&wifi_enable_h>; ++ reset-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_LOW>; ++ }; ++ ++ keys: gpio-keys { ++ compatible = "gpio-keys"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwr_key>; ++ autorepeat; ++ up-key { ++ gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_LOW>; ++ linux,code = <103>; ++ label = "key up"; ++ }; ++ down-key { ++ gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>; ++ linux,code = <108>; ++ label = "key down"; ++ }; ++ left-key { ++ gpios = <&gpio4 RK_PB3 GPIO_ACTIVE_LOW>; ++ linux,code = <106>; ++ label = "key left"; ++ }; ++ right-key { ++ gpios = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>; ++ linux,code = <107>; ++ label = "key right"; ++ }; ++ enter-key { ++ gpios = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>; ++ linux,code = <29>; ++ label = "key enter"; ++ }; ++ back-key { ++ gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>; ++ linux,code = <159>; ++ label = "key back"; ++ }; ++ reset-key { ++ gpios = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>; ++ linux,code = <2>; ++ label = "key reset"; ++ }; ++ }; ++ ++ gpio_leds: gpio-leds { ++ compatible = "gpio-leds"; ++ status = "okay"; ++ ++ board-led { ++ gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>; ++ pinctrl-0 = <&board_led>; ++ linux,default-trigger = "heartbeat"; ++ }; ++ }; ++ ++ board_antenna: board-antenna { ++ status = "okay"; ++ compatible = "regulator-fixed"; ++ enable-active-low; ++ gpio = <&gpio3 RK_PD3 GPIO_ACTIVE_LOW>; ++ regulator-always-on; ++ regulator-boot-on; ++ pinctrl-0 = <&ant_1>; ++ pinctrl-names = "default"; ++ regulator-name = "board_antenna"; ++ }; ++ ++ hdmi_sound: hdmi-sound { ++ compatible = "simple-audio-card"; ++ simple-audio-card,format = "i2s"; ++ simple-audio-card,mclk-fs = <128>; ++ simple-audio-card,name = "rockchip-hdmi0"; ++ ++ simple-audio-card,cpu { ++ sound-dai = <&i2s0_8ch>; ++ }; ++ simple-audio-card,codec { ++ sound-dai = <&hdmi>; ++ }; ++ }; ++ }; ++ ++ &cpu0 { ++ cpu-supply = <&vdd_cpu>; ++ }; ++ ++ &dfi { ++ status = "okay"; ++ }; ++ ++ &dmc { ++ center-supply = <&vdd_logic>; ++ status = "okay"; ++ }; ++ ++ &gpu { ++ mali-supply = <&vdd_gpu>; ++ status = "okay"; ++ }; ++ ++ &pmu_io_domains { ++ status = "okay"; ++ pmuio2-supply = <&vcc_3v3>; ++ vccio1-supply = <&vccio_acodec>; ++ vccio2-supply = <&vcc_1v8>; ++ vccio3-supply = <&vccio_sd>; ++ vccio4-supply = <&vcc_1v8>; ++ vccio5-supply = <&vcc_3v3>; ++ vccio6-supply = <&vcc_3v3>; ++ vccio7-supply = <&vcc_3v3>; ++ }; ++ ++ &i2c0 { ++ status = "okay"; ++ ++ vdd_cpu: rk860x@40 { ++ status = "okay"; ++ compatible = "silergy,syr827"; ++ reg = <0x40>; ++ vin-supply = <&vcc5v0_sys>; ++ regulator-compatible = "fan53555-reg"; ++ regulator-name = "vdd_cpu"; ++ regulator-min-microvolt = <712500>; ++ regulator-max-microvolt = <1390000>; ++ regulator-init-microvolt = <1000000>; ++ regulator-ramp-delay = <2300>; ++ fcs,suspend-voltage-selector = <1>; ++ regulator-boot-on; ++ regulator-always-on; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ rk817: pmic@20 { ++ compatible = "rockchip,rk817"; ++ reg = <0x20>; ++ interrupt-parent = <&gpio0>; ++ interrupts = <3 IRQ_TYPE_LEVEL_LOW>; ++ ++ pinctrl-names = "default", "pmic-sleep", ++ "pmic-power-off", "pmic-reset"; ++ pinctrl-0 = <&pmic_int>; ++ pinctrl-1 = <&soc_slppin_slp>, <&rk817_slppin_slp>; ++ pinctrl-2 = <&soc_slppin_gpio>, <&rk817_slppin_pwrdn>; ++ pinctrl-3 = <&soc_slppin_gpio>, <&rk817_slppin_rst>; ++ rockchip,system-power-controller; ++ wakeup-source; ++ #clock-cells = <1>; ++ clock-output-names = "rk808-clkout1", "rk808-clkout2"; ++ //fb-inner-reg-idxs = <2>; ++ /* 1: rst regs (default in codes), 0: rst the pmic */ ++ pmic-reset-func = <0>; ++ /* not save the PMIC_POWER_EN register in uboot */ ++ not-save-power-en = <1>; ++ ++ vcc1-supply = <&vcc_sys>; ++ vcc2-supply = <&vcc_sys>; ++ vcc3-supply = <&vcc_sys>; ++ vcc4-supply = <&vcc_sys>; ++ vcc5-supply = <&vcc_sys>; ++ vcc6-supply = <&vcc_sys>; ++ vcc7-supply = <&vcc_sys>; ++ vcc8-supply = <&vcc_sys>; ++ vcc9-supply = <&dcdc_boost>; ++ ++ pwrkey { ++ status = "okay"; ++ }; ++ ++ pinctrl_rk8xx: pinctrl_rk8xx { ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ rk817_slppin_null: rk817_slppin_null { ++ pins = "gpio_slp"; ++ function = "pin_fun0"; ++ }; ++ ++ rk817_slppin_slp: rk817_slppin_slp { ++ pins = "gpio_slp"; ++ function = "pin_fun1"; ++ }; ++ ++ rk817_slppin_pwrdn: rk817_slppin_pwrdn { ++ pins = "gpio_slp"; ++ function = "pin_fun2"; ++ }; ++ ++ rk817_slppin_rst: rk817_slppin_rst { ++ pins = "gpio_slp"; ++ function = "pin_fun3"; ++ }; ++ }; ++ ++ regulators { ++ vdd_logic: DCDC_REG1 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <500000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-init-microvolt = <900000>; ++ regulator-ramp-delay = <6001>; ++ regulator-initial-mode = <0x2>; ++ regulator-name = "vdd_logic"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ regulator-suspend-microvolt = <900000>; ++ }; ++ }; ++ ++ vdd_gpu: DCDC_REG2 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <500000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-init-microvolt = <900000>; ++ regulator-ramp-delay = <6001>; ++ regulator-initial-mode = <0x2>; ++ regulator-name = "vdd_gpu"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc_ddr: DCDC_REG3 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-initial-mode = <0x2>; ++ regulator-name = "vcc_ddr"; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ }; ++ }; ++ ++ vcc_3v3: DCDC_REG4 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-initial-mode = <0x2>; ++ regulator-name = "vcc_3v3"; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <3300000>; ++ }; ++ }; ++ ++ vcca1v8_pmu: LDO_REG1 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-name = "vcca1v8_pmu"; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <1800000>; ++ }; ++ }; ++ ++ vdda_0v9: LDO_REG2 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ regulator-name = "vdda_0v9"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vdda0v9_pmu: LDO_REG3 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ regulator-name = "vdda0v9_pmu"; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <900000>; ++ }; ++ }; ++ ++ vccio_acodec: LDO_REG4 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-name = "vccio_acodec"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vccio_sd: LDO_REG5 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-name = "vccio_sd"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc3v3_pmu: LDO_REG6 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-name = "vcc3v3_pmu"; ++ regulator-state-mem { ++ regulator-on-in-suspend; ++ regulator-suspend-microvolt = <3000000>; ++ }; ++ }; ++ ++ vcc_1v8: LDO_REG7 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-name = "vcc_1v8"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc1v8_dvp: LDO_REG8 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-name = "vcc1v8_dvp"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ vcc2v8_dvp: LDO_REG9 { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <2800000>; ++ regulator-max-microvolt = <2800000>; ++ regulator-name = "vcc2v8_dvp"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ dcdc_boost: BOOST { ++ regulator-always-on; ++ regulator-boot-on; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ regulator-name = "boost"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ ++ otg_switch: OTG_SWITCH { ++ regulator-name = "otg_switch"; ++ regulator-state-mem { ++ regulator-off-in-suspend; ++ }; ++ }; ++ }; ++ ++ rk817_codec: codec { ++ #sound-dai-cells = <0>; ++ compatible = "rockchip,rk817-codec"; ++ clocks = <&cru I2S1_MCLKOUT>; ++ clock-names = "mclk"; ++ assigned-clocks = <&cru I2S1_MCLKOUT>, <&cru I2S1_MCLK_TX_IOE>; ++ assigned-clock-rates = <12288000>; ++ assigned-clock-parents = <&cru I2S1_MCLKOUT_TX>, <&cru I2S1_MCLKOUT_TX>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s1m0_mclk>; ++ hp-volume = <20>; ++ spk-volume = <3>; ++ out-l2spk-r2hp; ++ //spk-ctl-gpios = <&gpio0 RK_PA6 GPIO_ACTIVE_HIGH>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ &i2s1_8ch { ++ status = "okay"; ++ rockchip,clk-trcm = <1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s1m0_sclktx ++ &i2s1m0_lrcktx ++ &i2s1m0_sdi0 ++ &i2s1m0_sdo0>; ++ }; ++ ++ &iep { ++ status = "okay"; ++ }; ++ ++ &iep_mmu { ++ status = "okay"; ++ }; ++ ++ &jpegd { ++ status = "okay"; ++ }; ++ ++ &jpegd_mmu { ++ status = "okay"; ++ }; ++ ++ &mpp_srv { ++ status = "okay"; ++ }; ++ ++ &rk_rga { ++ status = "okay"; ++ }; ++ ++ &rkvdec { ++ status = "okay"; ++ }; ++ ++ &rkvdec_mmu { ++ status = "okay"; ++ }; ++ ++ &rkvenc { ++ venc-supply = <&vdd_logic>; ++ status = "okay"; ++ }; ++ ++ &rkvenc_mmu { ++ status = "okay"; ++ }; ++ ++ &rknpu { ++ rknpu-supply = <&vdd_gpu>; ++ }; ++ ++ &saradc { ++ status = "okay"; ++ vref-supply = <&vcc_1v8>; ++ }; ++ ++ &sdhci { ++ bus-width = <8>; ++ supports-emmc; ++ non-removable; ++ max-frequency = <200000000>; ++ status = "okay"; ++ }; ++ ++ &sdmmc1 { ++ max-frequency = <150000000>; ++ supports-sdio; ++ bus-width = <4>; ++ disable-wp; ++ cap-sd-highspeed; ++ cap-sdio-irq; ++ keep-power-in-suspend; ++ non-removable; ++ rockchip,default-sample-phase = <90>; ++ mmc-pwrseq = <&sdio_pwrseq>; ++ non-removable; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>; ++ sd-uhs-sdr104; ++ status = "okay"; ++ }; ++ ++ &uart1 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn &uart1m0_rtsn>; ++ ++ bluetooth { ++ compatible = "brcm,bcm43438-bt"; ++ clocks = <&rk817 1>; ++ clock-names = "lpo"; ++ device-wakeup-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; ++ host-wakeup-gpios = <&gpio0 RK_PB3 GPIO_ACTIVE_HIGH>; ++ shutdown-gpios = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; ++ max-speed = <1500000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>; ++ vbat-supply = <&vcc3v3_sys>; ++ vddio-supply = <&vcc_1v8>; ++ }; ++ }; ++ ++ &gmac1 { ++ status = "disabled"; ++ }; ++ ++ &mdio1 { ++ status = "disabled"; ++ }; ++ ++ &sfc { ++ status = "okay"; ++ }; ++ ++ &tsadc { ++ status = "okay"; ++ }; ++ ++ &usb2phy0 { ++ status = "okay"; ++ }; ++ ++ &u2phy0_host { ++ status = "okay"; ++ }; ++ ++ &u2phy0_otg { ++ status = "okay"; ++ }; ++ ++ &u2phy1_host { ++ status = "okay"; ++ }; ++ ++ &u2phy1_otg { ++ status = "okay"; ++ }; ++ ++ &usb_host0_ehci { ++ status = "okay"; ++ }; ++ ++ &usb_host0_ohci { ++ status = "okay"; ++ }; ++ ++ &usb2phy1 { ++ status = "okay"; ++ }; ++ ++ &usb_host1_ehci { ++ status = "okay"; ++ }; ++ ++ &usb_host1_ohci { ++ status = "okay"; ++ }; ++ ++ &combphy1_usq { ++ status = "okay"; ++ }; ++ ++ &usbdrd30 { ++ status = "okay"; ++ }; ++ ++ &usbhost_dwc3 { ++ status = "okay"; ++ }; ++ ++ &usbhost30 { ++ status = "okay"; ++ }; ++ ++ &u2phy0_otg { ++ status = "okay"; ++ }; ++ ++ &usbdrd_dwc3 { ++ dr_mode = "host"; ++ extcon = <&usb2phy0>; ++ status = "okay"; ++ }; ++ ++ &vdpu { ++ status = "okay"; ++ }; ++ ++ &vdpu_mmu { ++ status = "okay"; ++ }; ++ ++ &vepu { ++ status = "okay"; ++ }; ++ ++ &vepu_mmu { ++ status = "okay"; ++ }; ++ ++ &vop { ++ status = "okay"; ++ assigned-clocks = <&cru DCLK_VOP0>, <&cru DCLK_VOP1>; ++ assigned-clock-parents = <&pmucru PLL_HPLL>, <&cru PLL_VPLL>; ++ }; ++ ++ &vop_mmu { ++ status = "okay"; ++ }; ++ ++ &i2c3 { ++ status = "okay"; ++ pinctrl-0 = <&i2c3m1_xfer>; ++ ++ usbc0: fusb302@22 { ++ compatible = "fcs,fusb302"; ++ reg = <0x22>; ++ interrupt-parent = <&gpio3>; ++ interrupts = ; ++ sel-gpios= <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbc0_int>; ++ }; ++ }; ++ ++ &reserved_memory { ++ ramoops: ramoops@110000 { ++ compatible = "ramoops"; ++ reg = <0x0 0x110000 0x0 0xf0000>; ++ record-size = <0x20000>; ++ console-size = <0x80000>; ++ ftrace-size = <0x00000>; ++ pmsg-size = <0x50000>; ++ }; ++ }; ++ ++ &rng { ++ status = "okay"; ++ }; ++ ++ &rockchip_suspend { ++ status = "okay"; ++ }; ++ ++ &gpio0 { ++ gpio-line-names = ++ /* GPIO0_A0-A3 */ ++ "", "", "", "", ++ /* GPIO0_A4-A7 */ ++ "", "", "", "", ++ ++ /* GPIO0_B0-B3 */ ++ "", "", "", "", ++ /* GPIO0_B4-B7 */ ++ "", "", "", "", ++ ++ /* GPIO0_C0-C3 */ ++ "", "", "", "", ++ /* GPIO0_C4-C7 */ ++ "", "", "", "", ++ ++ /* GPIO0_D0-D3 */ ++ "PIN_10", "PIN_8", "", "", ++ /* GPIO0_D4-D7 */ ++ "", "", "", ""; ++ }; ++ ++ &gpio1 { ++ gpio-line-names = ++ /* GPIO1_A0-A3 */ ++ "PIN_3", "PIN_5", "", "", ++ /* GPIO1_A4-A7 */ ++ "PIN_37", "", "", "", ++ ++ /* GPIO1_B0-B3 */ ++ "", "", "", "", ++ /* GPIO1_B4-B7 */ ++ "", "", "", "", ++ ++ /* GPIO1_C0-C3 */ ++ "", "", "", "", ++ /* GPIO1_C4-C7 */ ++ "", "", "", "", ++ ++ /* GPIO1_D0-D3 */ ++ "", "", "", "", ++ /* GPIO1_D4-D7 */ ++ "", "", "", ""; ++ }; ++ ++ &gpio2 { ++ gpio-line-names = ++ /* GPIO2_A0-A3 */ ++ "", "", "", "", ++ /* GPIO2_A4-A7 */ ++ "", "", "", "", ++ ++ /* GPIO2_B0-B3 */ ++ "", "", "", "", ++ /* GPIO2_B4-B7 */ ++ "", "", "", "", ++ ++ /* GPIO2_C0-C3 */ ++ "", "", "", "", ++ /* GPIO2_C4-C7 */ ++ "", "", "", "", ++ ++ /* GPIO2_D0-D3 */ ++ "", "", "", "", ++ /* GPIO2_D4-D7 */ ++ "", "", "", ""; ++ }; ++ ++ &gpio3 { ++ gpio-line-names = ++ /* GPIO3_A0-A3 */ ++ "", "PIN_11", "PIN_13", "PIN_12", ++ /* GPIO3_A4-A7 */ ++ "PIN_35", "PIN_40", "PIN_38", "PIN_36", ++ ++ /* GPIO3_B0-B3 */ ++ "PIN_15", "PIN_16", "PIN_18", "PIN_29", ++ /* GPIO3_B4-B7 */ ++ "PIN_31", "", "", "", ++ ++ /* GPIO3_C0-C3 */ ++ "", "PIN_22", "PIN_32", "PIN_33", ++ /* GPIO3_C4-C7 */ ++ "PIN_7", "", "", "", ++ ++ /* GPIO3_D0-D3 */ ++ "", "", "", "", ++ /* GPIO3_D4-D7 */ ++ "", "", "", ""; ++ }; ++ ++ &gpio4 { ++ gpio-line-names = ++ /* GPIO4_A0-A3 */ ++ "", "", "", "", ++ /* GPIO4_A4-A7 */ ++ "", "", "", "", ++ ++ /* GPIO4_B0-B3 */ ++ "", "", "PIN_27", "PIN_28", ++ /* GPIO4_B4-B7 */ ++ "", "", "", "", ++ ++ /* GPIO4_C0-C3 */ ++ "", "", "PIN_23", "PIN_19", ++ /* GPIO4_C4-C7 */ ++ "", "PIN_21", "PIN_24", "", ++ ++ /* GPIO4_D0-D3 */ ++ "", "PIN_26", "", "", ++ /* GPIO4_D4-D7 */ ++ "", "", "", ""; ++ }; ++ ++ &i2s0_8ch { ++ status = "okay"; ++ }; ++ ++ &display_subsystem { ++ status = "okay"; ++ }; ++ ++ &hdmi { ++ status = "okay"; ++ // preset_as_preferred; ++ // dirty fixing to 720p for now ++ preset_max_hdisplay = <1280>; ++ preset_max_vdisplay = <720>; ++ }; ++ ++ &hdmi_in_vp0 { ++ status = "okay"; ++ }; ++ ++ &hdmi_in_vp1 { ++ status = "disabled"; ++ }; ++ ++ &hdmi_sound { ++ status = "okay"; ++ }; ++ ++ &route_hdmi { ++ status = "okay"; ++ connect = <&vp0_out_hdmi>; ++ }; ++ ++ &sdmmc0 { ++ max-frequency = <150000000>; ++ supports-sd; ++ no-sdio; ++ no-mmc; ++ bus-width = <4>; ++ cap-mmc-highspeed; ++ cap-sd-highspeed; ++ disable-wp; ++ num-slots = <1>; ++ sd-uhs-sdr104; ++ vmmc-supply = <&vcc3v3_sys>; ++ vqmmc-supply = <&vccio_sd>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdmmc0_bus4 &sdmmc0_clk &sdmmc0_cmd &sdmmc0_det>; ++ status = "okay"; ++ }; ++ ++ &rockchip_suspend { ++ status = "okay"; ++ rockchip,sleep-debug-en = <1>; ++ rockchip,sleep-mode-config = < ++ (0 ++ | RKPM_SLP_CENTER_OFF ++ | RKPM_SLP_HW_PLLS_OFF ++ | RKPM_SLP_PMUALIVE_32K ++ | RKPM_SLP_32K_PVTM ++ ) ++ >; ++ rockchip,wakeup-config = < ++ (0 ++ | RKPM_GPIO_WKUP_EN ++ | RKPM_USB_WKUP_EN ++ ) ++ >; ++ }; ++ ++ &pinctrl { ++ pmic { ++ pmic_int: pmic_int { ++ rockchip,pins = ++ <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ ++ soc_slppin_gpio: soc_slppin_gpio { ++ rockchip,pins = ++ <0 RK_PA2 RK_FUNC_GPIO &pcfg_output_low_pull_down>; ++ }; ++ ++ soc_slppin_slp: soc_slppin_slp { ++ rockchip,pins = ++ <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ ++ soc_slppin_rst: soc_slppin_rst { ++ rockchip,pins = ++ <0 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ wifi { ++ wifi_enable_h: wifi-enable-h { ++ rockchip,pins = <0 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ wifi_host_wake_irq: wifi-host-wake-irq { ++ rockchip,pins = <0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ }; ++ ++ keys { ++ pwr_key: pwr-key { ++ rockchip,pins = ++ <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_up>, ++ <4 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>, ++ <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>, ++ <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++ ++ bt { ++ bt_enable_h: bt-enable-h { ++ rockchip,pins = <0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ bt_host_wake_l: bt-host-wake-l { ++ rockchip,pins = <0 RK_PB3 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ ++ bt_wake_l: bt-wake-l { ++ rockchip,pins = <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ leds { ++ board_led: board-led { ++ rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>; ++ }; ++ }; ++ ++ antenna { ++ ant_1: ant-1 { ++ rockchip,pins = <3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_down>; ++ }; ++ }; ++ ++ usb-typec { ++ usbc0_int: usbc0-int { ++ rockchip,pins = <3 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>; ++ }; ++ }; ++ ++ }; ++ +\ No newline at end of file diff --git a/scripts/patches/openhd_uart.patch b/scripts/patches/openhd_uart.patch new file mode 100644 index 0000000000000..06dadd118f12c --- /dev/null +++ b/scripts/patches/openhd_uart.patch @@ -0,0 +1,74 @@ +commit aa58e24f0d7f1e0b81ed40a400c381d2b6483374 +Author: Raphael <68374617+raphaelscholle@users.noreply.github.com> +Date: Mon Apr 22 01:27:12 2024 +0200 + + add uarts + +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi +index 68312e16cefb..740397f1720b 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-cm3.dtsi +@@ -711,6 +711,12 @@ bluetooth { + }; + }; + ++&uart2 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2m0_xfer>; ++}; ++ + &pinctrl { + pmic { + pmic_int: pmic_int { +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts +index cb42385aaaea..3c26bf516f6f 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-aic8800ds2.dts +@@ -100,6 +100,12 @@ &uart1 { + pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>; + }; + ++&uart2 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2m0_xfer>; ++}; ++ + &sdmmc1 { + supports-sdio; + bus-width = <4>; +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts +index adce697c8c45..b44573c41f9d 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero-3w-ap6212.dts +@@ -588,6 +588,12 @@ bluetooth { + vddio-supply = <&vcc_1v8>; + }; + }; ++ ++ &uart2 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2m0_xfer>; ++}; + + &gmac1 { + status = "disabled"; +diff --git a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts +index a8a02c1572c2..8375fa10df42 100644 +--- a/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts ++++ b/arch/arm64/boot/dts/rockchip/rk3566-radxa-zero3.dts +@@ -537,6 +537,12 @@ bluetooth { + }; + }; + ++&uart2 { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2m0_xfer>; ++}; ++ + &gmac1 { + phy-mode = "rgmii"; + clock_in_out = "output"; diff --git a/scripts/patches/openhd_utils.patch b/scripts/patches/openhd_utils.patch new file mode 100644 index 0000000000000..aa6a82ea9b7ed --- /dev/null +++ b/scripts/patches/openhd_utils.patch @@ -0,0 +1,49 @@ +commit 0f61633a6014d6bce330e39b7a5ab0f88da60de8 +Author: Raphael Scholle +Date: Fri Mar 15 23:56:31 2024 +0100 + + modify utils + +diff --git a/lib/utils.sh b/lib/utils.sh +index bdb1bcd..2b5dd86 100644 +--- a/lib/utils.sh ++++ b/lib/utils.sh +@@ -199,6 +199,38 @@ prepare_source() { + git reset --hard FETCH_HEAD + git clean -ffd + git switch --detach $origin/$BSP_BRANCH ++ ++ #openhd-update-wifi-card-drivers ++ echo "________OpenHD Part_________" ++ cd drivers/net/wireless/ ++ rm -Rf rtl8812au ++ rm -Rf rtl88x2bu ++ git clone https://github.com/openhd/rtl8812au/ ++ git clone https://github.com/openhd/rtl88x2bu/ ++ git clone https://github.com/openhd/rtl88x2eu/ ++ git clone https://github.com/openhd/rtl88x2bu/ -b initial52-1 rtl8852bu ++ cd rtl8812au ++ sed -i 's/CONFIG_PLATFORM_I386_PC = y/CONFIG_PLATFORM_I386_PC = n/' Makefile ++ sed -i 's/CONFIG_PLATFORM_ARM64_RPI = n/CONFIG_PLATFORM_ARM64_RPI = y/' Makefile ++ cd .. ++ cd rtl88x2bu ++ sed -i 's/CONFIG_PLATFORM_I386_PC = y/CONFIG_PLATFORM_I386_PC = n/' Makefile ++ sed -i 's/CONFIG_PLATFORM_ARM64_RPI = n/CONFIG_PLATFORM_ARM64_RPI = y/' Makefile ++ cd .. ++ cd rtl88x2eu ++ sed -i 's/CONFIG_PLATFORM_I386_PC = y/CONFIG_PLATFORM_I386_PC = n/' Makefile ++ sed -i 's/CONFIG_PLATFORM_ARM64_RPI = n/CONFIG_PLATFORM_ARM64_RPI = y/' Makefile ++ cd .. ++ cd rtl8852bu ++ sed -i 's/aarch64\.l/arm64/' Makefile ++ cd ../../../../ ++ patch -p1 -i scripts/patches/openhd_drm-rock5.patch ++ patch -p1 -i scripts/patches/openhd_wifi.patch ++ #patch -p1 -i scripts/patches/openhd_cams.patch ++ patch -p1 -i scripts/patches/openhd_i2c.patch ++ #patch -p1 -i scripts/patches/openhd_cm3.patch ++ #patch -p1 -i scripts/patches/openhd_uart.patch ++ echo "________OpenHD END_________" + fi + + if $LONG_VERSION diff --git a/scripts/patches/openhd_wifi.patch b/scripts/patches/openhd_wifi.patch new file mode 100644 index 0000000000000..a5e4ffb72b155 --- /dev/null +++ b/scripts/patches/openhd_wifi.patch @@ -0,0 +1,68 @@ +commit 482a59913b76cfb1cb88242f1db8e5f5b7dddccd +Author: Raphael <68374617+raphaelscholle@users.noreply.github.com> +Date: Sat Aug 24 00:15:24 2024 +0200 + + insert openhd wifi + +diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig +index 7569882f3474..48724e783e15 100644 +--- a/arch/arm64/configs/rockchip_linux_defconfig ++++ b/arch/arm64/configs/rockchip_linux_defconfig +@@ -691,6 +691,9 @@ CONFIG_WIFI_BUILD_MODULE=y + CONFIG_AP6XXX=m + CONFIG_RTL8852BE=m + CONFIG_RTL8852BU=m ++CONFIG_RTL8812AU=m ++CONFIG_RTL8822BU=m ++CONFIG_RTL8822EU=m + CONFIG_USB_NET_RNDIS_WLAN=y + # CONFIG_IEEE802154_DRIVERS is not set + CONFIG_LTE=y +diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig +index 46f5cc40788a..0c20dd854372 100644 +--- a/drivers/net/wireless/Kconfig ++++ b/drivers/net/wireless/Kconfig +@@ -49,6 +49,9 @@ source "drivers/net/wireless/ti/Kconfig" + source "drivers/net/wireless/zydas/Kconfig" + source "drivers/net/wireless/quantenna/Kconfig" + source "drivers/net/wireless/rockchip_wlan/Kconfig" ++source "drivers/net/wireless/rtl8812au/Kconfig" ++source "drivers/net/wireless/rtl88x2bu/Kconfig" ++source "drivers/net/wireless/rtl88x2eu/Kconfig" + + config PCMCIA_RAYCS + tristate "Aviator/Raytheon 2.4GHz wireless support" +diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile +index 5671364ae98d..e88039ae12f4 100644 +--- a/drivers/net/wireless/Makefile ++++ b/drivers/net/wireless/Makefile +@@ -31,3 +31,6 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o + + obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o + obj-$(CONFIG_WL_ROCKCHIP) += rockchip_wlan/ ++obj-m += rtl8812au/ ++obj-m += rtl88x2bu/ ++obj-m += rtl88x2eu/ +diff --git a/drivers/net/wireless/realtek/Kconfig b/drivers/net/wireless/realtek/Kconfig +index 474843277fa1..d8dbd0596395 100644 +--- a/drivers/net/wireless/realtek/Kconfig ++++ b/drivers/net/wireless/realtek/Kconfig +@@ -14,7 +14,5 @@ if WLAN_VENDOR_REALTEK + + source "drivers/net/wireless/realtek/rtl818x/Kconfig" + source "drivers/net/wireless/realtek/rtlwifi/Kconfig" +-source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" +-source "drivers/net/wireless/realtek/rtw88/Kconfig" + + endif # WLAN_VENDOR_REALTEK +diff --git a/drivers/net/wireless/realtek/Makefile b/drivers/net/wireless/realtek/Makefile +index 888b5d594e79..2ba7e756f1dc 100644 +--- a/drivers/net/wireless/realtek/Makefile ++++ b/drivers/net/wireless/realtek/Makefile +@@ -6,6 +6,4 @@ + obj-$(CONFIG_RTL8180) += rtl818x/ + obj-$(CONFIG_RTL8187) += rtl818x/ + obj-$(CONFIG_RTLWIFI) += rtlwifi/ +-obj-$(CONFIG_RTL8XXXU) += rtl8xxxu/ +-obj-$(CONFIG_RTW88) += rtw88/ +